🐛 Bug: Previous Agent Response duplicates on New Question with Google ADK
♻️ Reproduction Steps
Environment: Copilotkit.ai + ADK Agent (Python)
Agent Framework: Google ADK Agent (Integrated via AG-UI middleware)
CopilotKit Version: @copilotkit/react-ui: 1.10.6
Google ADK Version/SDK: 1.18
Steps to Reproduce:
Open the CopilotKit-powered chat interface.
First Question: Ask a question that triggers a result. (Example: "generate logs from 2025/11/10 3pm to 4pm").
Wait for the response to fully complete and display (e.g., "Log Retrieval Complete" and the links).
Second Question: Immediately enter and submit a new, different question. (Example : "What is the value of gateway.public.host").
Observe: Just before the agent starts streaming the new answer, the previous response ("Log Retrieval Complete" + links) reappears momentarily, followed by the new, correct response.
✅ Expected Behavior
The chat interface should immediately clear the previous agent response or display a new, clean loading/streaming state when a new user question is submitted. The previous message content should not flash or display before the new response starts.
❌ Actual Behavior
The previous response is displayed again when the new request is initiated, causing a visual duplication.
𝌚 CopilotKit Version
├── @copilotkit/[email protected]
├── @copilotkit/[email protected]
├── @copilotkit/[email protected]
📄 Logs (Optional)
This issue occurs because the previous assistant response is not cleared from the UI before the new streaming response begins, resulting in a brief flash of the old content. The CopilotKit chat implementation tracks loading state but does not explicitly clear previous assistant messages when a new question is submitted, especially when using the Google ADK agent.
Root Cause of the Flashing Issue
The flashing occurs because:
• No explicit message clearing: Previous assistant messages remain visible in the UI until new streaming content arrives to replace them
• Loading state doesn't clear content: While Chat.tsx tracks isLoading state, it doesn't trigger clearing of previous assistant messages
• GoogleGenerativeAI adapter issues: The GoogleGenerativeAIAdapter has known issues with handling empty AIMessages that may contribute to state inconsistencies
• Streaming behavior: Messages persist in state until replaced by new streaming content, causing the brief "flash" of old content
Current Implementation Analysis
The current chat implementation:
• Chat.tsx tracks loading state with isLoading and prevIsLoading useRef
• safelySendMessage function clears suggestions but not previous assistant messages
• Messages component displays content without clearing previous responses during streaming transitions
Solutions to Fix the Flashing
1. Clear Previous Assistant Message Before New Stream
// In your chat implementation, clear the last assistant message when starting new stream
const handleNewMessage = async (message) => {
// Clear the last assistant message before starting new stream
setMessages(prev => prev.filter(msg =>
!(msg.role === 'assistant' && msg.id === lastAssistantMessageId)
));
// Then send the new message
await sendMessage(message);
};
2. Show Loading State Instead of Previous Content
// Modify message rendering to show loading placeholder
const renderMessage = (message) => {
if (message.role === 'assistant' && isLoading && message.id === lastAssistantMessageId) {
return <LoadingSpinner />; // Show loading instead of previous content
}
return <MessageContent content={message.content} />;
};
3. Use the Built-in Reset Functionality
The useCopilotChat hook provides a reset() function:
const { reset } = useCopilotChat();
// Call reset before sending new message if you want to clear all previous messages
const handleNewConversation = () => {
reset(); // "Clear all messages and reset chat state completely"
// Then send your new message
};
4. Implement Proper Loading State Management
// Leverage the existing loading state tracking
useEffect(() => {
if (isLoading && !prevIsLoading.current) {
// Chat started - optionally clear previous assistant message
handleChatStart();
}
prevIsLoading.current = isLoading;
}, [isLoading]);
Recommended Implementation
The most effective approach would be to:
• Modify the chat flow to clear the previous assistant message when isLoading becomes true
• Show a loading indicator in place of the cleared message
• Address the GoogleGenerativeAI adapter specific issues if they persist
Documentation References
- CopilotChat Component - Main chat component documentation
- useCopilotChat Hook - Chat state management hook with reset functionality
Note: This is a known UI state management issue where the loading state tracking exists but doesn't implement message clearing before new streaming responses begin.
Was this helpful?
If this solution worked for you, please click on the appropriate option below to help us improve:
After some tests, I discovered that this "bug" occurs when I use McpToolset :
tableau_agent = LlmAgent(
name="TableauAgent",
model=GEMINI_2_FLASH,
instruction="""
You are the maintenance agent for Server A (Tableau).
* Log Retrieval: If the user asks for logs, call the tableau_get_logs tool. Date Time format must be "mm/dd/yyyy H:mm"
* Status: If the user asks for status, call the tableau_get_status tool.
* Parameter: If the user asks for a parameter, call the get_parameter tool.
* Return the tool output directly, without conversational phrases.
""",
tools=[
# McpToolset gives the agent access to tool_tableau_get_logs, tool_tableau_get_status, tool_get_parameter
McpToolset(
connection_params=SseServerParams(url="http://xxxgcptbbfe1:8001/sse")
),
],
output_key="tableau_output",
)
If I don't use McpToolset, I don't see the last output repeated after the next request before to display the new output
I observed the same issue, also using ADK. Only happen if using the built-in tool or MCPToolset. Testing using the ADK Web the response is returning normally.
The graphql payload:
{"operationName":"generateCopilotResponse","query":"mutation generateCopilotResponse($data: GenerateCopilotResponseInput!, $properties: JSONObject) {\n generateCopilotResponse(data: $data, properties: $properties) {\n threadId\n runId\n extensions {\n openaiAssistantAPI {\n runId\n threadId\n __typename\n }\n __typename\n }\n ... on CopilotResponse @defer {\n status {\n ... on BaseResponseStatus {\n code\n __typename\n }\n ... on FailedResponseStatus {\n reason\n details\n __typename\n }\n __typename\n }\n __typename\n }\n messages @stream {\n __typename\n ... on BaseMessageOutput {\n id\n createdAt\n __typename\n }\n ... on BaseMessageOutput @defer {\n status {\n ... on SuccessMessageStatus {\n code\n __typename\n }\n ... on FailedMessageStatus {\n code\n reason\n __typename\n }\n ... on PendingMessageStatus {\n code\n __typename\n }\n __typename\n }\n __typename\n }\n ... on TextMessageOutput {\n content @stream\n role\n parentMessageId\n __typename\n }\n ... on ImageMessageOutput {\n format\n bytes\n role\n parentMessageId\n __typename\n }\n ... on ActionExecutionMessageOutput {\n name\n arguments @stream\n parentMessageId\n __typename\n }\n ... on ResultMessageOutput {\n result\n actionExecutionId\n actionName\n __typename\n }\n ... on AgentStateMessageOutput {\n threadId\n state\n running\n agentName\n nodeName\n runId\n active\n role\n __typename\n }\n }\n metaEvents @stream {\n ... on LangGraphInterruptEvent {\n type\n name\n value\n __typename\n }\n ... on CopilotKitLangGraphInterruptEvent {\n type\n name\n data {\n messages {\n __typename\n ... on BaseMessageOutput {\n id\n createdAt\n __typename\n }\n ... on BaseMessageOutput @defer {\n status {\n ... on SuccessMessageStatus {\n code\n __typename\n }\n ... on FailedMessageStatus {\n code\n reason\n __typename\n }\n ... on PendingMessageStatus {\n code\n __typename\n }\n __typename\n }\n __typename\n }\n ... on TextMessageOutput {\n content\n role\n parentMessageId\n __typename\n }\n ... on ActionExecutionMessageOutput {\n name\n arguments\n parentMessageId\n __typename\n }\n ... on ResultMessageOutput {\n result\n actionExecutionId\n actionName\n __typename\n }\n }\n value\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n}","variables":{"data":{"agentSession":{"agentName":"root_agent"},"agentStates":[{"agentName":"root_agent","config":"{}","state":"{}"}],"context":[],"extensions":{},"forwardedParameters":{},"frontend":{"actions":[],"url":"http://localhost:3000/a//copilot"},"messages":[{"createdAt":"2025-11-17T00:31:55.584Z","id":"ck-f9a32a0f-a781-40e9-b076-e2ff092c4534","textMessage":{"content":"\nPlease act as an efficient, competent, conscientious, and industrious professional assistant.\n\nHelp the user achieve their goals, and you do so in a way that is as efficient as possible, without unnecessary fluff, but also without sacrificing professionalism.\nAlways be polite and respectful, and prefer brevity over verbosity.\n\nThe user has provided you with the following context:\n```\n\n\n\n```\n\nThey have also provided you with functions you can call to initiate actions on their behalf, or functions you can call to receive more information.\n\nPlease assist them as best you can.\n\nYou can ask them for clarifying questions if needed, but don't be annoying about it. If you can reasonably 'fill in the blanks' yourself, do so.\n\nIf you would like to call a function, call it without saying anything else.\nIn case of a function error:\n- If this error stems from incorrect function parameters or syntax, you may retry with corrected arguments.\n- If the error's source is unclear or seems unrelated to your input, do not attempt further retries.\n","role":"system"}},{"createdAt":"2025-11-17T00:31:04.358Z","id":"ck-41069f27-bd8d-4c31-8847-0c05d00224eb","textMessage":{"content":"search the internet what is the height of mount everest","role":"user"}},{"actionExecutionMessage":{"arguments":"{\"request\":\"height of Mount Everest\"}","name":"google_search_agent","parentMessageId":null},"createdAt":"2025-11-17T00:31:15.640Z","id":"16771dc5-0328-4777-a0e5-670ddf8459d2"},{"createdAt":"2025-11-17T00:31:15.640Z","id":"result-16771dc5-0328-4777-a0e5-670ddf8459d2","resultMessage":{"actionExecutionId":"16771dc5-0328-4777-a0e5-670ddf8459d2","actionName":"google_search_agent","result":"{\"result\": \"The official height of Mount Everest is 8,848.86 meters (29,031.7 feet). This measurement was jointly announced by Chinese and Nepali authorities in 2020. The updated height includes both the rock base and the snow cap of the mountain. Before this joint declaration, the widely accepted height for many years was 8,848 meters (29,029 feet).\"}"}},{"createdAt":"2025-11-17T00:31:15.640Z","id":"d19ffc17-c847-401e-8987-91a7c754e0cd","textMessage":{"content":"The height of Mount Everest is **8,848.86 meters (29,031.7 feet)**, as jointly announced by Chinese and Nepali authorities in 2020. This figure includes both the rock summit and its snow‑cap. \n\n*Source: Internet search results.*","parentMessageId":null,"role":"assistant"}},{"createdAt":"2025-11-17T00:31:55.584Z","id":"ck-62758c4b-0826-4538-b374-e18248ff99de","textMessage":{"content":"search the internet why is the sky blue?","role":"user"}}],"metaEvents":[],"metadata":{"requestType":"Chat"},"runId":null,"threadId":"ff12704f-0f39-48b8-9506-527723fc7431"},"properties":{}}}
Hi @chewcw did you open a support ticket on your end as soon as you noticed the problem? Did you find a workaround?
👋 Thanks for your feedback!
I've escalated this issue to our development team. Someone from our team will review your request and provide additional assistance shortly.
If this is blocking production, please book a meeting with us at https://cal.com/nathan-tarbert-copilotkit/15min
Hi @chewcw did you open a support ticket on your end as soon as you noticed the problem? Did you find a workaround?
I was under time constraints to prepare a demo, so I had to move quickly, but unfortunately I couldn’t find a workaround. I tried the suggestions provided by the support bot in the thread, but to no avail. The assistant message ID is different for each new “duplicate” message, which means I can’t use the suggested lastAssistantMessageId approach. I’d be happy to contribute if I were capable, but I’m still very new to Agentic application development, so I’ll need to explore this further for now.
Facing the same issue even without using McpToolset. Haven't found any workarounds yet!
Hello everyone! We are also experiencing the same issue using Google ADK. It seems the previous response message from the agent is being repeated in the subsequent user request. In building the frontend, we tried to follow the guidelines provided here; are we missing something?
Hi! I’ve temporarily solved the issue by using the middleware here suggested, paying attention to the additional configuration required for CopilotKit integration. I hope this can be helpful in the meantime.
I have the same issue and it looks like it is in the AG-UI adapter for ADK. See the issue: https://github.com/ag-ui-protocol/ag-ui/issues/712 . Try downgrading to 0.3.1 as suggested in the other issue.
I have the same issue and it looks like it is in the AG-UI adapter for ADK. See the issue: ag-ui-protocol/ag-ui#712 . Try downgrading to 0.3.1 as suggested in the other issue.
It worked fine! It really soves the issue!
Thanks for your comments, I will test the workaround suggested by @JeffBerger