mirror of
https://github.com/browseros-ai/BrowserOS.git
synced 2026-05-14 16:14:28 +00:00
fix: filter empty-parts messages to prevent follow-up conversation crash (#402)
* fix: filter out messages with empty parts to prevent follow-up crash When an assistant response is interrupted or errors before producing content, a UIMessage with empty parts remains in the chat state. On the next send, the AI SDK validates all messages and rejects the empty-parts message with "Message must contain at least one part". This filters them out when not streaming and adds a safety guard in formatConversationHistory. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: filter empty-parts messages before persisting to storage Addresses race condition where the save effect could persist messages with empty parts before the cleanup effect's state update applies. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -304,6 +304,15 @@ export const useChatSession = () => {
|
||||
}),
|
||||
})
|
||||
|
||||
// Remove messages with empty parts (e.g. interrupted assistant responses)
|
||||
// to prevent AI SDK validation errors on subsequent sends
|
||||
useEffect(() => {
|
||||
if (status === 'streaming') return
|
||||
if (messages.some((m) => !m.parts?.length)) {
|
||||
setMessages(messages.filter((m) => m.parts?.length > 0))
|
||||
}
|
||||
}, [messages, status, setMessages])
|
||||
|
||||
useNotifyActiveTab({
|
||||
messages,
|
||||
status,
|
||||
@@ -370,15 +379,14 @@ export const useChatSession = () => {
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: only need to run when messages change
|
||||
useEffect(() => {
|
||||
messagesRef.current = messages
|
||||
if (messages.length > 0) {
|
||||
// Local storage: save on every change (including during streaming)
|
||||
// Remote: only save when not streaming to avoid partial message saves
|
||||
const messagesToSave = messages.filter((m) => m.parts?.length > 0)
|
||||
if (messagesToSave.length > 0) {
|
||||
if (isLoggedIn) {
|
||||
if (status !== 'streaming') {
|
||||
saveRemoteConversation(conversationIdRef.current, messages)
|
||||
saveRemoteConversation(conversationIdRef.current, messagesToSave)
|
||||
}
|
||||
} else {
|
||||
saveLocalConversation(conversationIdRef.current, messages)
|
||||
saveLocalConversation(conversationIdRef.current, messagesToSave)
|
||||
}
|
||||
}
|
||||
}, [messages, isLoggedIn, status])
|
||||
|
||||
@@ -17,6 +17,7 @@ export function formatConversationHistory(
|
||||
|
||||
return recentMessages
|
||||
.map((msg) => {
|
||||
if (!msg.parts?.length) return null
|
||||
const role: 'user' | 'assistant' =
|
||||
msg.role === 'user' ? 'user' : 'assistant'
|
||||
const textContent = msg.parts
|
||||
|
||||
Reference in New Issue
Block a user