Short answer: treat the response as failed, not finished. A stream that ends before message_stop was cut off mid-flight; retry the whole request and find what closed the connection. 1. Log the last event type you received and whether a message_delta with a stop_reason arrived. If message_delta arrived but message_stop did not, the break is almost always in transport (proxy, load balancer, or client read loop), not in generation. Expected: you can name the exact event the stream died on. 2. Check the middleboxes. Corporate proxies, CDNs, and ingress controllers commonly kill or buffer long SSE connections on idle timeouts. Disable response buffering for the streaming route and raise idle timeouts above your longest generation. Expected: streams survive past the previous cut-off point. 3. Check your own read loop. Breaking out of the event loop early, a read timeout set for the whole request instead of per-event silence, or a client disconnect downstream all produce this error. Use a silence threshold (no event for N seconds) rather than a total deadline for long outputs. 4. Retry the request from scratch. There is no resume: a truncated stream cannot be continued, and the partial output should be discarded or clearly marked partial, never stored as a completed message. Expected: the retry either completes with message_stop or fails loudly. 5. If it reproduces on short outputs with no proxy in between, capture the request id and check the provider status page; a server-side disconnect is possible but is the last explanation, not the first. Root cause: message_stop is the only completion signal in the Anthropic streaming protocol. Anything that closes the HTTP connection early (idle-timeout intermediaries are the most common, dropped streams run at a fraction of a percent even direct) leaves the client holding a partial message, and strict consumers correctly refuse to call it done. When to use: SDK or hand-rolled SSE consumers of the Anthropic Messages API seeing truncated streaming responses. When NOT to use: a stream that ends with an explicit error event is an API error with its own type and message; handle that instead. A stop_reason of max_tokens in message_delta is a complete, valid stream, not this error. Variant phrasings: "stream ended without message_stop", "claude stream truncated", "anthropic sse connection closed early". Edge cases: tool-use streams carry more events and longer silent gaps while a large input_json accumulates; size your silence threshold for that. In browsers, an EventSource auto-reconnect can silently restart a request and duplicate output.

Context: A healthy Anthropic Messages stream ends with a fixed event sequence: message_start, content_block events, message_delta (which carries stop_reason and final usage), then message_stop. Code that assembles the final message, or a wrapper that validates the stream, reports this error when the connection closes before that terminal message_stop arrives. The partial text received so far is not a completed response, even when it looks finished.