fix: preserve partial content when synthesis stream drops mid-response

When the final synthesis stream dropped mid-way (connection error,
timeout), the onError handler discarded the streaming message entirely
and showed only the error. If the model had already streamed 90% of a
good answer, the user lost all of it.

Now: if the streaming message has partial content, it is preserved and
finalized with an interrupted note appended. The error is still shown
as a separate message so the user knows what happened.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
2026-08-19 12:59:00 +05:30
parent 7384945b6c
commit 03ee9718a4

View File

@@ -1571,13 +1571,35 @@ export function ChatWorkspace() {
}
},
onError: (err: Error) => {
// Clean up streaming placeholder if the stream errored
// Clean up streaming placeholder if the stream errored.
// If the streaming message already has partial content (model streamed
// most of the answer before the connection dropped), preserve it and
// finalize it instead of discarding — losing a 90% complete response
// is worse than showing it with an error note appended.
streamCancelRef.current = null
const streamMsgId = streamingMsgIdRef.current
streamingMsgIdRef.current = null
clearBridgeDraft(sessionId)
if (streamMsgId) {
setMessages(prev => prev.filter(m => m.id !== streamMsgId))
let preserved = false
setMessages(prev => {
const streamMsg = prev.find(m => m.id === streamMsgId)
if (streamMsg?.content && streamMsg.content.trim().length > 0) {
// Preserve partial content — finalize the streaming message
preserved = true
return prev.map(m =>
m.id === streamMsgId
? { ...m, isStreaming: false, content: m.content + '\n\n---\n*Response interrupted — connection dropped before completion.*' }
: m,
)
}
// No useful content — remove the empty placeholder
return prev.filter(m => m.id !== streamMsgId)
})
if (preserved) {
if (err.name === 'AbortError') return
// Still show the error as a separate message so the user knows what happened
}
}
if (err.name === 'AbortError') return
const isTimeout = err.name === 'TimeoutError'