langchain-ai/langchain · error · TypeError
SyncTextProjection received a non-string final value
Error message
SyncTextProjection received a non-string final value
What it means
`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.
Source
Thrown at libs/core/langchain_core/language_models/chat_model_stream.py:321
raise TypeError(msg)
super().complete(final_value)
def __iter__(self) -> Iterator[str]:
"""Yield text deltas, raising if a producer supplied a non-string value."""
for delta in super().__iter__():
if not isinstance(delta, str):
msg = "SyncTextProjection received a non-string delta"
raise TypeError(msg)
yield delta
def get(self) -> str:
"""Drain and return the full accumulated string, or empty if unfinished."""
value = super().get()
if value is None:
return ""
if not isinstance(value, str):
msg = "SyncTextProjection received a non-string final value"
raise TypeError(msg)
return value
def __str__(self) -> str:
"""Drain and return the full accumulated string."""
return self.get()
def __bool__(self) -> bool:
"""Return whether any deltas have been pushed."""
return len(self._deltas) > 0
def __repr__(self) -> str:
"""Return repr of the accumulated text so far."""
if self._final_set:
return repr(self._final_value)
return repr("".join(self._deltas))
# ---------------------------------------------------------------------------View on GitHub (pinned to e32fa9a52e)
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.
Example fix
# before projection.SyncProjection.complete(final_msg) # untyped completion value = projection.get() # TypeError # after projection.complete(final_msg.text or "") value = projection.get()
Defensive patterns
Strategy: type-guard
Validate before calling
value = SyncProjection.get(projection) # raw, unvalidated
final = value if isinstance(value, str) else None
if final is None and value is not None:
raise TypeError("producer completed text projection with non-string") Type guard
def completed_text_is_valid(proj) -> bool:
"""True when the projection's final value is a str (or not yet complete)."""
v = SyncProjection.get(proj)
return v is None or isinstance(v, str) Try / catch
null
Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- SyncTextProjection requires a string delta
- SyncTextProjection requires a string final value
- SyncTextProjection received a non-string delta
- AsyncTextProjection received a non-string delta
- AsyncTextProjection requires a string delta
AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14).
Data as JSON: /api/errors/ed5c9b73fc3d6f9f.
Report an issue: GitHub.