fix(streaming): detect premature connection close and surface error to user

When a reverse proxy (e.g., Tailscale Serve, nginx) closes the SSE
connection after an idle timeout, the reader returns { done: true }
cleanly. Previously this was treated as a successful completion, causing
the thinking indicator to disappear and a retry button to appear with
no error message — silently discarding any in-progress backend work.

Now: if the stream ends without a 'done' event AND no response text was
received while in 'accepted' or 'active' phase, call markFailed() with
a clear message instead of finishStream(). This surfaces an error toast
and persists the error state so the user knows what happened.

Fixes #512.

Validation: pnpm build passes, use-streaming-message tests (4/4) pass.
This commit is contained in:
Aurora release bot
2026-05-24 12:55:02 -04:00
parent a47846d354
commit 77aae80707

View File

@@ -983,7 +983,21 @@ export function useStreamingMessage(options: UseStreamingMessageOptions = {}) {
const lifecyclePhase = lifecyclePhaseRef.current as StreamLifecyclePhase
if (!finishedRef.current && lifecyclePhase !== 'handoff') {
finishStream()
// If the stream ended cleanly (no 'done' event) but we never received
// any response text, treat it as a failure rather than a successful
// empty completion. This happens when a proxy (e.g., Tailscale Serve)
// closes the connection after an idle timeout — the reader returns
// { done: true } but the model was still generating. Fixes #512.
if (
!fullTextRef.current &&
(lifecyclePhase === 'accepted' || lifecyclePhase === 'active')
) {
markFailed(
'Connection closed before response was received. The backend may still be processing — check server logs or retry.',
)
} else {
finishStream()
}
}
} catch (err) {
if ((err as Error).name === 'AbortError') {