{"record":{"id":"a6b23e970edd20c4","repo":"langchain-ai/langchain","slug":"v2-stream-finished-without-producing-a-message","errorCode":null,"errorMessage":"v2 stream finished without producing a message","messagePattern":"v2 stream finished without producing a message","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"libs/core/langchain_core/language_models/chat_models.py","lineNumber":1951,"sourceCode":"            **kwargs,\n        ):\n            stream_accum = ChatModelStream(\n                message_id=(\n                    f\"{LC_ID_PREFIX}-{run_manager.run_id}\" if run_manager else None\n                )\n            )\n            assert run_manager is not None  # noqa: S101\n            for _event in self._iter_v2_events(\n                messages,\n                run_manager=run_manager,\n                stream=stream_accum,\n                stop=stop,\n                **kwargs,\n            ):\n                pass\n            if stream_accum.output_message is None:\n                msg = \"v2 stream finished without producing a message\"\n                raise RuntimeError(msg)\n            result = ChatResult(\n                generations=[ChatGeneration(message=stream_accum.output_message)]\n            )\n        # If stream is not explicitly set, check if implicitly requested by\n        # astream_events() or astream_log(). Bail out if _stream not implemented\n        elif self._should_stream(\n            async_api=False,\n            run_manager=run_manager,\n            **kwargs,\n        ):\n            chunks: list[ChatGenerationChunk] = []\n            run_id: str | None = (\n                f\"{LC_ID_PREFIX}-{run_manager.run_id}\" if run_manager else None\n            )\n            yielded = False\n            index = -1\n            index_type = \"\"\n            for chunk in self._stream(messages, stop=stop, **kwargs):","sourceCodeStart":1933,"sourceCodeEnd":1969,"githubUrl":"https://github.com/langchain-ai/langchain/blob/e32fa9a52eab3b61ad7a45399bfde59b3e580fc4/libs/core/langchain_core/language_models/chat_models.py#L1933-L1969","documentation":"`RuntimeError` raised in sync `_generate_with_cache` after driving the v2 protocol-event stream to completion: the stream accumulator's `output_message` was still `None`. The v2 streaming path (native event generator or the `_stream` compat bridge) is expected to assemble at least one message; reaching the end without one is an invariant violation.","triggerScenarios":"A v2-opted-in callback handler (`_V2StreamingCallbackHandler`) triggers protocol streaming, but the model's event generator / `_stream` produces no message events — empty streams, streams that only emit non-message events, or a broken compat bridge in a custom subclass.","commonSituations":"Custom chat models that implement `_stream` but yield nothing for some inputs; providers returning empty completions; LangSmith/tracing handlers that opt into v2 streaming while the model produces zero events.","solutions":["Ensure the model's `_stream` (or native event generator) emits at least one message-producing chunk/`message_start`-style event.","Reproduce with v2 streaming disabled (no v2 handlers attached) to confirm the model itself returns a message; if not, fix the model.","Check for upstream API errors that end the stream early — the error may mask the real cause; inspect the raw stream.","Upgrade `langchain-core`: v2 bridge regressions have been fixed across releases."],"exampleFix":"# before\ndef _stream(self, messages, stop=None, **kw):\n    return\n    yield  # v2 path ends with output_message None\n\n# after\ndef _stream(self, messages, stop=None, **kw):\n    yield ChatGenerationChunk(message=AIMessageChunk(content=self._generate(messages, stop=stop).generations[0].message.content))","handlingStrategy":"validation","validationCode":"def stream_yields_message(model, messages) -> bool:\n    for _ in model._stream(messages):\n        return True\n    return False","typeGuard":null,"tryCatchPattern":"try:\n    result = model.invoke(messages)\nexcept RuntimeError as e:\n    if \"v2 stream finished without producing\" in str(e):\n        # detach v2 handlers / fall back to plain invoke without callbacks\n        result = model.invoke(messages, callbacks=None)\n    else:\n        raise","preventionTips":["Guarantee custom `_stream` yields at least one chunk for every input path.","Smoke-test custom models with tracing handlers attached (they can enable v2 streaming).","Keep langchain-core updated for v2 bridge fixes."],"tags":["streaming","v2-protocol","empty-stream","chat-model"],"backgroundTag":null,"analyzedSha":"e32fa9a52eab3b61ad7a45399bfde59b3e580fc4","analyzedAt":"2026-08-14T18:42:09.092Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}