{"record":{"id":"d53a1e1b0bcf2044","repo":"langchain-ai/langchain","slug":"asynctextprojection-received-a-non-string-final-va","errorCode":null,"errorMessage":"AsyncTextProjection received a non-string final value","messagePattern":"AsyncTextProjection received a non-string final value","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"libs/core/langchain_core/language_models/chat_model_stream.py","lineNumber":547,"sourceCode":"            raise TypeError(msg)\n        super().complete(final_value)\n\n    def __aiter__(self) -> _AsyncTextProjectionIterator:\n        \"\"\"Return an async iterator over text deltas.\"\"\"\n        return _AsyncTextProjectionIterator(self)\n\n    def __await__(self) -> Generator[Any, None, str]:\n        \"\"\"Await the full accumulated text.\"\"\"\n        return self._await_text_impl().__await__()\n\n    async def _await_text_impl(self) -> str:\n        \"\"\"Return accumulated text or raise if the final value is not a string.\"\"\"\n        value = await self._await_impl()\n        if value is None:\n            return \"\"\n        if not isinstance(value, str):\n            msg = \"AsyncTextProjection received a non-string final value\"\n            raise TypeError(msg)\n        return value\n\n\n# ---------------------------------------------------------------------------\n# Sync stream\n# ---------------------------------------------------------------------------\n\n\nclass _ChatModelStreamBase:\n    \"\"\"Shared state and event dispatch for chat-model streams.\n\n    Holds accumulated protocol state (text, reasoning, tool calls,\n    usage, metadata) and the event-dispatch machinery that drives the\n    typed projections. `ChatModelStream` (sync) and\n    `AsyncChatModelStream` (async) inherit from this base and add the\n    projection types and consumer APIs for their flavor.\n    \"\"\"\n","sourceCodeStart":529,"sourceCodeEnd":565,"githubUrl":"https://github.com/langchain-ai/langchain/blob/e32fa9a52eab3b61ad7a45399bfde59b3e580fc4/libs/core/langchain_core/language_models/chat_model_stream.py#L529-L565","documentation":"Raised when awaiting an `AsyncTextProjection` (e.g. `stream.text()` on an async chat-model stream) and the stream's final aggregated value is neither `None` nor a `str`. The text projection is a typed view over the stream's completion value; this guard fires when the underlying model produced a final value of an unexpected type, such as a list of content blocks instead of plain text. It exists to surface contract violations early instead of silently returning garbage.","triggerScenarios":"Awaiting `await stream.text()` (or the projection's `__await__`) on an async chat-model stream whose final aggregated value is a non-string, e.g. a provider returning multimodal `content` as a list of blocks, or a custom `_astream` implementation that completes with a non-string value.","commonSituations":"Using a custom chat model or a provider that emits multimodal / structured content while consuming the stream through the text-only projection; a subclass overriding `_await_impl` with a wrong return type; partial refactors of the streaming internals.","solutions":["If the model returns multimodal content (list blocks), consume the message objects (`stream.output` / iterating events) instead of the text projection.","If you control the model subclass, make its final aggregated value a plain `str` (or `None`) so the text projection contract holds.","Inspect the raw final value by awaiting the underlying stream directly to confirm which type is being produced before choosing a projection.","If you never expect this state, wrap the await in `try/except TypeError` and fall back to reading the assembled `AIMessage`."],"exampleFix":"// before\nconst text = await stream.text();  // raises TypeError on list content\n\n# after (Python)\ntext = await stream.text() if isinstance(msg := stream.output.content, str) else extract_text(msg.content)","handlingStrategy":"type-guard","validationCode":"final = await stream._await_impl() if hasattr(stream, \"_await_impl\") else None\nif final is not None and not isinstance(final, str):\n    raise TypeError(\"expecting text stream\")","typeGuard":"def is_text_projection_safe(value: object) -> bool:\n    return value is None or isinstance(value, str)","tryCatchPattern":"try:\n    text = await stream.text()\nexcept TypeError as e:\n    if \"non-string final value\" in str(e):\n        msg = stream.output  # fall back to full message\n        text = msg.content if isinstance(msg.content, str) else str(msg.content)\n    else:\n        raise","preventionTips":["Prefer `stream.output.content` when the model may return multimodal content.","Keep custom `_astream` implementations producing string-aggregatable final values.","Unit-test the text projection against your model's actual content shape."],"tags":["streaming","async","type-mismatch","chat-model"],"backgroundTag":null,"analyzedSha":"e32fa9a52eab3b61ad7a45399bfde59b3e580fc4","analyzedAt":"2026-08-14T18:42:09.092Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}