langchain-ai/langchain · error · TypeError
SyncTextProjection requires a string final value
Error message
SyncTextProjection requires a string final value
What it means
`TypeError` from `SyncTextProjection.complete`: the final accumulated value supplied by the producer is not a `str` (e.g. a message object, `None`, or bytes). `complete(final_value)` closes the projection with the definitive text; passing anything else breaks every consumer (`get`, `__str__`, iteration), so it is rejected immediately.
Source
Thrown at libs/core/langchain_core/language_models/chat_model_stream.py:303
Adds typed string producers and consumers, plus `__str__`, `__bool__`,
`__repr__` for ergonomic use with `.text` and `.reasoning` projections.
"""
__slots__ = ()
def push(self, delta: str) -> None:
"""Append a text delta."""
if not isinstance(cast("Any", delta), str):
msg = "SyncTextProjection requires a string delta"
raise TypeError(msg)
super().push(delta)
def complete(self, final_value: str) -> None:
"""Set the final accumulated text and mark the projection as done."""
if not isinstance(cast("Any", final_value), str):
msg = "SyncTextProjection requires a string final value"
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)View on GitHub (pinned to e32fa9a52e)
Solutions
- Always complete with a string; for empty streams use `complete("")`.
- Convert explicitly at the completion site: `projection.complete(msg.content if isinstance(msg.content, str) else "")`.
- Audit producers that branch on stream termination to ensure every path passes a string.
Example fix
# before projection.complete(final_message) # AIMessage, not str # after projection.complete(final_message.text or "")
Defensive patterns
Strategy: type-guard
Validate before calling
final = final_value if isinstance(final_value, str) else "" projection.complete(final)
Type guard
def is_final_text(v: object) -> bool:
"""True when v is acceptable as a SyncTextProjection final value."""
return isinstance(v, str) Try / catch
null
Prevention
- Always complete with a str; use "" for empty streams
- Complete at exactly one place in the producer (a finally block) with a validated string
- Never call the untyped base SyncProjection.complete on a text projection
When it happens
Trigger: Producer code calling `projection.complete(final_text)` with a non-string — e.g. `complete(message)` where `message` is an `AIMessage`, or `complete(None)` when a stream produced no final text.
Common situations: Custom model adapters completing `.text` with the full message object instead of `message.content`; defensive `complete(result or None)` patterns where `None` flows in when the stream ended empty; refactors from dict-based buffers to the typed projection.
Related errors
- SyncTextProjection requires a string delta
- SyncTextProjection received a non-string delta
- SyncTextProjection received a non-string final value
- 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/30c0c6b10270416f.
Report an issue: GitHub.