The issue occurs due to a race condition between component re-mounting and agent connection establishment when switching threads.
- User switches threads →
currentThreadIdchanges - ChatInterface re-mounts due to key prop:
key={currentUser.userId}-${currentThreadId} - New component instance starts with:
historyMessages = undefinedcanUseAgentChat = falseisLoadingHistory = true
- useAgent hook called with new connection name:
${currentUser.userId}-${currentThreadId} - useAgentChat hook called with
initialMessages: undefined - Parallel execution:
- useEffect fetches thread history (async)
- Agent connection establishes (async)
- User may send message before both complete
- First message sent before agent is fully ready
- Message persistence fails or saves to wrong location
The key prop forces complete re-mount:
<ChatInterface
key={`${currentUser.userId}-${currentThreadId}`} // Forces re-mount
currentThreadId={currentThreadId}
// ...
/>// Agent connection name set immediately
const agent = useAgent({
agent: "chat",
name: `${currentUser?.userId}-${currentThreadId}`, // ✅ Correct
});
// But canUseAgentChat depends on agent.agent being ready
useEffect(() => {
setCanUseAgentChat(
enabled && !!currentUser?.userId && !!currentThreadId && !!agent.agent // ⚠️ May not be ready
);
}, [enabled, currentUser?.userId, currentThreadId, agent.agent]);// History starts as undefined
const [historyMessages, setHistoryMessages] = useState<Message[] | undefined>(undefined);
// useAgentChat called immediately with undefined initialMessages
const agentChatResult = useAgentChat({
agent: agent,
initialMessages: historyMessages, // ⚠️ undefined on first render
// ...
});
// History loaded asynchronously in parallel
useEffect(() => {
// Fetch thread history...
.then((data) => {
setHistoryMessages(Array.isArray(data) ? data : []); // ⚠️ After useAgentChat already called
});
}, [enabled, currentUser?.userId, currentThreadId, setCurrentUser]);// In Chat.onChatMessage()
let threadId = "default";
if (this.name) {
// ⚠️ this.name might not be set yet
const parts = this.name.split("-");
if (parts.length >= 2) {
const extractedThreadId = parts.slice(1).join("-").trim();
threadId = extractedThreadId || "default";
}
}✅ Tested extraction logic - works perfectly for all cases:
"user123-default"→"default""user123-thread_abc123"→"thread_abc123""user123-thread_2024-01-15-session-abc"→"thread_2024-01-15-session-abc"
❌ ChatInterface re-mounts on every thread switch ❌ Agent connection and history loading happen in parallel ❌ User can send message before setup completes
Remove the key prop and handle thread switching within the same component instance:
// Instead of:
<ChatInterface key={`${currentUser.userId}-${currentThreadId}`} ... />
// Use:
<ChatInterface currentThreadId={currentThreadId} ... />Wait for both agent connection and history loading before enabling chat:
const isReady =
canUseAgentChat && !isLoadingHistory && historyMessages !== undefined;Add logging to verify agent.name is set correctly before first message:
// Add debug logging in onChatMessage
console.log("Chat agent name:", this.name);
console.log("Extracted thread ID:", threadId);Disable input until agent and history are fully loaded:
const canSendMessage = isReady && !isAgentLoading && agentInput.trim();- High: Remove component re-mounting (Solution 1)
- High: Wait for both agent and history ready (Solution 2)
- Medium: Add proper loading states and disable input (Solution 4)
- Low: Add debug logging for verification (Solution 3)
- Switch to a new thread
- Immediately try to send a message
- Verify message is saved to correct thread storage
- Check that subsequent messages work correctly
The issue is most likely to occur when users quickly switch threads and immediately send messages before the agent connection is fully established.