{"record":{"id":"ed5c9b73fc3d6f9f","repo":"langchain-ai/langchain","slug":"synctextprojection-received-a-non-string-final-val","errorCode":null,"errorMessage":"SyncTextProjection received a non-string final value","messagePattern":"SyncTextProjection 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":321,"sourceCode":"            raise TypeError(msg)\n        super().complete(final_value)\n\n    def __iter__(self) -> Iterator[str]:\n        \"\"\"Yield text deltas, raising if a producer supplied a non-string value.\"\"\"\n        for delta in super().__iter__():\n            if not isinstance(delta, str):\n                msg = \"SyncTextProjection received a non-string delta\"\n                raise TypeError(msg)\n            yield delta\n\n    def get(self) -> str:\n        \"\"\"Drain and return the full accumulated string, or empty if unfinished.\"\"\"\n        value = super().get()\n        if value is None:\n            return \"\"\n        if not isinstance(value, str):\n            msg = \"SyncTextProjection received a non-string final value\"\n            raise TypeError(msg)\n        return value\n\n    def __str__(self) -> str:\n        \"\"\"Drain and return the full accumulated string.\"\"\"\n        return self.get()\n\n    def __bool__(self) -> bool:\n        \"\"\"Return whether any deltas have been pushed.\"\"\"\n        return len(self._deltas) > 0\n\n    def __repr__(self) -> str:\n        \"\"\"Return repr of the accumulated text so far.\"\"\"\n        if self._final_set:\n            return repr(self._final_value)\n        return repr(\"\".join(self._deltas))\n\n\n# ---------------------------------------------------------------------------","sourceCodeStart":303,"sourceCodeEnd":339,"githubUrl":"https://github.com/langchain-ai/langchain/blob/e32fa9a52eab3b61ad7a45399bfde59b3e580fc4/libs/core/langchain_core/language_models/chat_model_stream.py#L303-L339","documentation":"`TypeError` from `SyncTextProjection.get()` (also reached via `str(projection)`): the projection has finished and its stored final value is not a string. `get()` returns the accumulated text once complete; a non-string final value means the producer corrupted the state (e.g. via the untyped base-class `complete`), and consumers get a hard failure instead of silently stringifying garbage.","triggerScenarios":"A producer calling the base `SyncProjection.complete(non_str)` on a text projection (bypassing the typed override), then consumer code calling `projection.get()` or `str(projection)` after completion.","commonSituations":"Subclasses overriding `complete` without the isinstance guard; adapter code that completes with a message object via the untyped base; state left inconsistent when an exception path completes the projection with a sentinel.","solutions":["Fix the producer to call the typed `SyncTextProjection.complete(final_str)` — it validates on entry (errorIndex 74's guard).","Never invoke `SyncProjection.complete` directly on a text projection.","If you consume a projection you did not produce, validate with `isinstance(value, str)` on the raw value before relying on `get()` in non-critical paths."],"exampleFix":"# before\nprojection.SyncProjection.complete(final_msg)  # untyped completion\nvalue = projection.get()  # TypeError\n\n# after\nprojection.complete(final_msg.text or \"\")\nvalue = projection.get()","handlingStrategy":"type-guard","validationCode":"value = SyncProjection.get(projection)  # raw, unvalidated\nfinal = value if isinstance(value, str) else None\nif final is None and value is not None:\n    raise TypeError(\"producer completed text projection with non-string\")","typeGuard":"def completed_text_is_valid(proj) -> bool:\n    \"\"\"True when the projection's final value is a str (or not yet complete).\"\"\"\n    v = SyncProjection.get(proj)\n    return v is None or isinstance(v, str)","tryCatchPattern":"null","preventionTips":["Complete via the typed complete() only","Use str(projection) in reprs/logs so corruption surfaces at first use, not deep in consumers","Test the get()/str() path after producer error branches"],"tags":["streaming","typeerror","internal-api"],"backgroundTag":null,"analyzedSha":"e32fa9a52eab3b61ad7a45399bfde59b3e580fc4","analyzedAt":"2026-08-14T18:42:09.092Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}