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

  1. Fix the producer to call the typed `SyncTextProjection.complete(final_str)` — it validates on entry (errorIndex 74's guard).
  2. Never invoke `SyncProjection.complete` directly on a text projection.
  3. 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

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


AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14). Data as JSON: /api/errors/ed5c9b73fc3d6f9f. Report an issue: GitHub.