langchain-ai/langchain · error · RuntimeError
Stream finished without producing a message
Error message
Stream finished without producing a message
What it means
`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.
Source
Thrown at libs/core/langchain_core/language_models/chat_model_stream.py:1233
return self._reasoning_proj
@property
def tool_calls(self) -> SyncProjection:
"""Tool calls — iterable of `ToolCallChunk` deltas.
`.get()` returns finalized `list[ToolCall]`.
"""
return self._tool_calls_proj
@property
def output(self) -> AIMessage:
"""Assembled `AIMessage` — blocks until the stream finishes."""
self._drain()
if self._error is not None:
raise self._error
if self._output_message is None:
msg = "Stream finished without producing a message"
raise RuntimeError(msg)
return self._output_message
# -- Raw event iteration (replay buffer) -------------------------------
def __iter__(self) -> Iterator[MessagesData]:
"""Iterate raw protocol events with replay-buffer semantics."""
if self._ensure_started is not None:
self._ensure_started()
cursor = 0
while True:
if cursor < len(self._events):
yield self._events[cursor]
cursor += 1
elif self._error is not None:
raise self._error
elif self._done:
return
elif self._request_more is not None:View on GitHub (pinned to e32fa9a52e)
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.
Example fix
# before
async def _stream(self, messages, **kw):
return
yield # empty stream -> RuntimeError on .output
# after
async def _stream(self, messages, **kw):
yield ChatGenerationChunk(message=AIMessageChunk(content="done")) Defensive patterns
Strategy: try-catch
Validate before calling
events = list(stream) # or track chunks seen
if not events:
raise ValueError("stream produced no events; refusing to read .output") Type guard
def stream_has_output(stream) -> bool:
stream._drain()
return stream._output_message is not None Try / catch
try:
msg = stream.output
except RuntimeError as e:
if "without producing a message" in str(e):
msg = AIMessage(content="") # or surface a domain-specific error
else:
raise Prevention
- 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.
When it happens
Trigger: 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`.
Common situations: 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.
Related errors
- v2 stream finished without producing a message
- AsyncTextProjection received a non-string final value
- No generations found in stream.
- SyncTextProjection requires a string delta
- SyncTextProjection requires a string final value
AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14).
Data as JSON: /api/errors/aeb45b5c80173f73.
Report an issue: GitHub.