{"record":{"id":"80a18e81c3594115","repo":"Hmbown/CodeWhale","slug":"chat-completions-stream-closed-before-done-or-finish-reason","errorCode":null,"errorMessage":"Chat Completions stream closed before [DONE] or finish_reason","messagePattern":"Chat Completions stream closed before \\[DONE\\] or finish_reason","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/client/chat.rs","lineNumber":1688,"sourceCode":"                                yield Ok(event);\n                            }\n                        }\n                    }\n                }\n            }\n\n            // Close any open blocks — content_index points to the\n            // currently active open block (it is only incremented\n            // *after* a block is closed, not when opened).\n            if thinking_started || text_started {\n                yield Ok(StreamEvent::ContentBlockStop { index: content_index });\n            }\n\n            release_stream_buffer(byte_buf);\n            if !stream_failed && (saw_done || saw_finish_reason) {\n                yield Ok(StreamEvent::MessageStop);\n            } else if !stream_failed {\n                yield Err(anyhow::anyhow!(\n                    \"Chat Completions stream closed before [DONE] or finish_reason\"\n                ));\n            }\n        };\n\n        Ok(Pin::from(Box::new(stream)\n            as Box<\n                dyn futures_util::Stream<Item = Result<StreamEvent>> + Send,\n            >))\n    }\n}\n\n// === Chat Completions Helpers ===\n\n#[cfg(test)]\npub(super) fn build_chat_messages(\n    system: Option<&SystemPrompt>,\n    messages: &[Message],","sourceCodeStart":1670,"sourceCodeEnd":1706,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/client/chat.rs#L1670-L1706","documentation":"After the SSE stream ends normally (no transport failure), the code checks whether a `[DONE]` sentinel or a `finish_reason` was observed. If neither appeared, the stream closed prematurely — the generation never formally terminated — so this error is yielded instead of `MessageStop`.","triggerScenarios":"The server closes the connection after partial output without sending `data: [DONE]` and without any choice carrying a `finish_reason` — e.g. server-side abort, max-tokens kill without a final chunk, proxy closing idle keep-alive early, or an OpenAI-compatible server that ends streams uncleanly.","commonSituations":"vLLM/Ollama or other self-hosted servers dropping the final chunk under load; reverse proxies (nginx) closing streams on idle timeouts; provider outages truncating generations.","solutions":["Treat as an incomplete generation: check whether the assistant message content is usable or should be retried","Check server/proxy (nginx `proxy_read_timeout`, LB idle) timeouts and raise them","Verify the OpenAI-compatible server sends a terminating `finish_reason` chunk and `[DONE]`","Retry the request; enable streaming retry logic"],"exampleFix":"// before: nginx closing long idle streams\nproxy_read_timeout 60s;\n// after\nproxy_read_timeout 300s;\nproxy_buffering off;","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"closed before [DONE]\") => {\n        // incomplete generation: retry, or surface partial output with a warning\n        retry_with_backoff(e);\n    }\n    other => propagate(other),\n}","preventionTips":["Raise nginx/LB proxy_read_timeout and disable proxy_buffering for SSE","Verify the server sends a final finish_reason chunk plus [DONE]","Enable stream-level retry for clean reconnects","Monitor provider status pages for mid-stream abort incidents"],"tags":["sse","streaming","chat-completions","truncated-response"],"backgroundTag":"truncated-response","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}