{"record":{"id":"aeb45b5c80173f73","repo":"langchain-ai/langchain","slug":"stream-finished-without-producing-a-message","errorCode":null,"errorMessage":"Stream finished without producing a message","messagePattern":"Stream finished without producing a message","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"libs/core/langchain_core/language_models/chat_model_stream.py","lineNumber":1233,"sourceCode":"        return self._reasoning_proj\n\n    @property\n    def tool_calls(self) -> SyncProjection:\n        \"\"\"Tool calls — iterable of `ToolCallChunk` deltas.\n\n        `.get()` returns finalized `list[ToolCall]`.\n        \"\"\"\n        return self._tool_calls_proj\n\n    @property\n    def output(self) -> AIMessage:\n        \"\"\"Assembled `AIMessage` — blocks until the stream finishes.\"\"\"\n        self._drain()\n        if self._error is not None:\n            raise self._error\n        if self._output_message is None:\n            msg = \"Stream finished without producing a message\"\n            raise RuntimeError(msg)\n        return self._output_message\n\n    # -- Raw event iteration (replay buffer) -------------------------------\n\n    def __iter__(self) -> Iterator[MessagesData]:\n        \"\"\"Iterate raw protocol events with replay-buffer semantics.\"\"\"\n        if self._ensure_started is not None:\n            self._ensure_started()\n        cursor = 0\n        while True:\n            if cursor < len(self._events):\n                yield self._events[cursor]\n                cursor += 1\n            elif self._error is not None:\n                raise self._error\n            elif self._done:\n                return\n            elif self._request_more is not None:","sourceCodeStart":1215,"sourceCodeEnd":1251,"githubUrl":"https://github.com/langchain-ai/langchain/blob/e32fa9a52eab3b61ad7a45399bfde59b3e580fc4/libs/core/langchain_core/language_models/chat_model_stream.py#L1215-L1251","documentation":"`RuntimeError` raised by the `output` property of a sync chat-model stream accumulator after the stream is fully drained but no `AIMessage` was assembled. The property blocks until the stream finishes (`_drain()`), re-raises any stream error, and treats a finished stream with no output message as an internal invariant violation.","triggerScenarios":"Accessing `stream.output` on a stream that completed without ever producing a message — e.g. a `_stream`/protocol implementation that yields no events or only empty chunks, or a stream that was exhausted before any message-start event arrived. Also raised if the stream ended immediately due to an upstream short-circuit that did not set `_output_message`.","commonSituations":"Custom chat model subclasses whose `_stream` yields nothing; mock/fake streams in tests that forget to emit a message; providers that return an empty stream on unusual API responses; misconfigured protocol event generators.","solutions":["Ensure the underlying `_stream` (or v2 event generator) yields at least one chunk that produces a message, even an empty `AIMessageChunk`.","Check `stream._error` / `_drain()` behavior first: an upstream error can end the stream early and leave output unset — fix the upstream error.","In tests with fake streams, yield a minimal `ChatGenerationChunk(message=AIMessageChunk(content=\"\"))`.","Guard access: only read `.output` after confirming events were produced."],"exampleFix":"# before\nasync def _stream(self, messages, **kw):\n    return\n    yield  # empty stream -> RuntimeError on .output\n\n# after\nasync def _stream(self, messages, **kw):\n    yield ChatGenerationChunk(message=AIMessageChunk(content=\"done\"))","handlingStrategy":"try-catch","validationCode":"events = list(stream)  # or track chunks seen\nif not events:\n    raise ValueError(\"stream produced no events; refusing to read .output\")","typeGuard":"def stream_has_output(stream) -> bool:\n    stream._drain()\n    return stream._output_message is not None","tryCatchPattern":"try:\n    msg = stream.output\nexcept RuntimeError as e:\n    if \"without producing a message\" in str(e):\n        msg = AIMessage(content=\"\")  # or surface a domain-specific error\n    else:\n        raise","preventionTips":["Never return empty generators from `_stream` overrides.","In tests, always yield at least one chunk from fakes.","Check the provider's raw response when streams are unexpectedly empty."],"tags":["streaming","empty-stream","chat-model","invariant"],"backgroundTag":null,"analyzedSha":"e32fa9a52eab3b61ad7a45399bfde59b3e580fc4","analyzedAt":"2026-08-14T18:42:09.092Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}