From 03ee9718a4bbc471b623b81ccf5f27703c27f22b Mon Sep 17 00:00:00 2001 From: mohiit1502 Date: Wed, 19 Aug 2026 12:59:00 +0530 Subject: [PATCH] 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> --- src/components/layout/ChatWorkspace/index.tsx | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/components/layout/ChatWorkspace/index.tsx b/src/components/layout/ChatWorkspace/index.tsx index dc60dea..bb53bab 100644 --- a/src/components/layout/ChatWorkspace/index.tsx +++ b/src/components/layout/ChatWorkspace/index.tsx @@ -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'