Why is my LLM stream empty? A field guide to broken SSE responses A developer has identified four common failure modes that cause empty LLM streams from OpenAI-compatible APIs, including reasoning-only responses, missing finish_reason, malformed SSE framing, and truncated tool calls. To address these issues, the developer built an open-source diagnostic toolkit called agent-stream-doctor that records and analyzes raw streams to pinpoint the failure class automatically. If you have ever called an OpenAI-compatible API with streaming enabled and received... nothing , you are not alone. No error, no exception — just a stream that "completes" successfully while your UI stays empty. After debugging dozens of these cases — and building an open-source toolkit to automate the diagnosis — I keep seeing the same four failure modes . Here is the field guide I wish I had. Some models emit their entire answer inside a reasoning channel the "thinking" part and mark the actual content channel as empty. The stream works . Token usage is reported. Your parser is happy. Your UI shows nothing. python What arrives: {"delta": {"reasoning content": "Let me analyze this..."}, ...} {"delta": {"content": ""}, "finish reason": "stop"} The fix: always inspect the delta fields your model actually uses, not just content. If your client only reads choices 0 .delta.content, a reasoning-only response is indistinguishable from an empty one. 2. Missing finish reason When a proxy or router truncates the final chunk, finish reason quietly disappears — and many client libraries silently drop the message instead of raising. The fix: treat a missing finish reason as a red flag, not a quirk. Log it. Alert on it. A stream that ends without stop, length, or tool calls did not end — it was cut. 3. Malformed SSE framing SSE looks trivial: lines of data: {...} ending with data: DONE . But: multi-byte UTF-8 characters can be split across chunk boundaries some proxies rewrite or strip the data: prefix chunks can arrive after DONE , or the stream can end without it Each of these breaks parsers quietly — you lose characters in the middle of words, or the client hangs waiting for a terminator that never comes. The fix: log the raw frames before parsing. When something looks wrong downstream, the raw log is the only witness that tells the truth. 4. Truncated tool calls Agents assemble tool calls from multiple deltas. If the stream dies halfway, you are left with partial tool call deltas that never merge into a complete call — and an agent that hangs forever waiting for arguments that will never arrive. The fix: accumulate arguments across deltas, validate the assembled call, and set a timeout on "hanging" tool states. The workflow that catches all four Every one of these bugs shares one property: the protocol layer lies to you. The stream reports success while the payload is broken. So the fix is always the same: Observe the raw stream first. Reason about the protocol second. That is exactly why I built agent-stream-doctor — an open-source diagnostic toolkit Python, zero dependencies for the core that records raw OpenAI-compatible streams and pinpoints the failure class automatically: empty streams & reasoning-only responses missing / inconsistent finish reason malformed SSE lines and broken DONE sentinels incomplete tool calls and truncated arguments pip install git+