Stream Closed Before Completed
Four causes, one hop-by-hop method
A streaming response cuts off mid-way with the error text "stream closed before completed" — there are four common causes, and they're not the same kind of problem at all; a hop-by-hop check is the only way to find the right one.
Four key facts
Four common causes
An upstream rate-limit frame, a timeout mismatch in the chain, the client disconnecting, or a rewrite layer swallowing the terminal event — they look similar but have completely different root causes.
The core of the triage method
The path from client to model service usually crosses multiple hops (client → gateway → load balancer → upstream); you have to check each hop to find where the cutoff actually happened.
Whether the terminal event arrives
Many streaming protocols mark a normal finish with a terminal event (like [DONE]); if the stream cuts off before that shows up, it was cut short rather than completing normally.
The most commonly overlooked cause
Clients, gateways, and load balancers usually each set their own timeout; if any single hop's timeout is shorter than the model's actual generation time, that hop will forcibly cut the stream.
What each of the four causes actually is
First, an upstream rate-limit frame: the model service hits a rate limit mid-generation and inserts an interrupt signal that ends the generation early — the client just sees the stream cut off before finishing. Second, a timeout mismatch: the request crosses multiple hops — client, proxy, gateway, load balancer — and if any single hop's configured timeout is shorter than the model's actual generation time, that hop will actively cut the connection even though the model side is still generating normally. Third, the client disconnecting (client gone): the user closed the page or cancelled the request mid-way, or the client's container/process got reclaimed — this kind of cutoff usually leaves a 'peer closed connection'-style entry in server logs, and points the opposite direction from the first two. Fourth, a rewrite layer swallowing the terminal event: some reverse proxies, CDNs, or rewriting middleware reprocess the streaming response body, and if their implementation has a bug, they can forward the actual content fine while dropping the terminal event that marks 'generation finished normally' (like SSE's [DONE]) — the client then thinks the connection broke abnormally, when the model side actually finished generating just fine.
Why this triage method is worth remembering
Of the four causes, the first two (rate-limit frame, timeout mismatch) are the ones most often misdiagnosed as 'our network is unstable.' The third (client disconnect) often gets wrongly blamed on 'the model is unreliable' from the server side. The fourth (rewrite layer swallowing the terminal event) is the sneakiest, because the content the client receives looks complete — it's just missing that one telltale closing signal — so it tends to get dismissed as a 'random bug' rather than a systemic issue.
Timeline
Streaming responses (SSE and similar protocols) and their terminal-event mechanism are a long-standing standard design; all four cutoff causes have existed as long as streaming has been common.
Agentic workflows increase the number of long-running streaming generations, making poorly-tuned timeouts anywhere in the chain more likely to surface.
The sneakiest cause — a rewrite layer swallowing the terminal event — is showing up more often as reverse-proxy/CDN middleware grows more complex.
Confirmed vs. common misreading
Confirmed
The "stream closed before completed" message shows up consistently in public developer discussion; all four causes (rate-limit frame, timeout mismatch, client disconnect, rewrite layer swallowing the event) are each independently documented and require separate diagnosis.
Common misreading
Many people's first instinct on a stream cutoff is 'the model is unreliable' or 'the vendor's service is broken' — but in practice, timeout mismatches and rewrite-layer bugs, both of which are client-/middleware-side configuration or implementation issues, account for a substantial share of cases.
How to tell the four causes apart
Server-side cutoff (rate-limit frame / timeout)
Server logs will show this generation got rate-limited or that some hop's timeout triggered the cutoff; the tell is that the same request often completes fine at a different time or with lower concurrency.
Client-side issue (disconnect / rewrite layer swallowing the event)
Server logs show the generation actually finished normally — the problem is the client disconnecting too early, or a rewrite layer in between failing to forward the terminal event. This needs checking the client/proxy configuration, not the model service.
The hop-by-hop triage method
Step 1: check server logs — did this generation actually run to completion, and was it rate-limited? If the server records show it finished normally, the problem is very likely on some hop between client and server. Step 2: check timeout settings hop by hop — client timeout, CDN/gateway timeout, load balancer timeout — and find which one is shorter than the model's actual generation time. Step 3: if you suspect a rewrite layer swallowing the event, directly compare the raw stream the server sent against what the client actually received, and check whether the terminal event got lost in the middle. Step 4: if it's the client disconnecting on purpose (e.g. the user cancelled manually), that isn't a fault at all — you just need to handle that cancellation case correctly in your application logic instead of misreading it as a system failure.
What to do on QCode
QCode's edge nodes align timeouts for streaming responses, reducing cases where a mismatched timeout on one hop wrongly cuts off a long-running generation; if your own proxy or rewrite middleware has a similar issue, you can also route through a QCode endpoint as a stopgap while you diagnose it.
FAQ
How do I know if a stream cutoff is on my side?
Check server logs first if you can get them: if they show the generation finished normally, the problem is very likely on some hop between client and server (timeout settings, a rewrite layer, etc.) — something you can diagnose and fix.
Why does the same request sometimes work and sometimes cut off?
The most common reason is a timeout mismatch: generation time naturally varies, and whenever it happens to exceed the shortest timeout threshold anywhere in the chain, that request gets cut off, while faster requests complete fine.
Does a client disconnecting count as a fault?
Strictly speaking, no — it's normal user behavior (cancelling a request, closing the page) or a client environment change (the process getting reclaimed); it just needs to be correctly identified and handled in your application logic, not chased as a system failure.
What's the fastest way to diagnose a rewrite layer swallowing the terminal event?
Capture traffic or add logging directly comparing what the server actually streamed against what the client ultimately received — if the server sent the terminal event but the client never got it, the problem is in a rewrite/forwarding layer in between.
What should I check first when I hit this error?
First check server logs for rate limits or errors; if there's nothing there, check the timeout settings on the client, gateway, and load balancer — these two steps rule out three of the four causes.
Is this the same as 429/529?
No. 429/529 mean the request gets rejected before generation even starts; stream closed before completed means generation already started and the stream was already open, but it got cut off before finishing — the two happen at completely different stages.
Sources
The four causes are compiled from public developer discussion, the standard behavior of streaming protocols like SSE, and the general operational practice of hop-by-hop timeout triage; this page makes no claims about any single vendor's private implementation. Compiled 2026-08-27.
Stream cutoffs shouldn't slow down delivery
QCode's edge nodes align timeouts for streaming responses, reducing mismatched-hop cutoffs on long generations.
Related reading
Context Length Exceeded: the complete guide
Another commonly misdiagnosed error — a useful comparison.
The complete guide to querying your API key usage
How to systematically review your own call history and anomalies.
Claude 529 vs 429 vs Weekly Limit
Another set of easily-confused errors, told apart.
This page is general cross-vendor technical explanation and makes no claims about any single vendor's private implementation. Actual behavior depends on the specific model, client, and proxy chain you use.