langchain-ai/langchain · error · TypeError

SyncTextProjection received a non-string delta

Error message

SyncTextProjection received a non-string delta

What it means

`TypeError` raised while iterating a `SyncTextProjection` (`for delta in projection`): a delta already sitting in the buffer is not a string. This is a second line of defense — even if a non-string slipped past `push` (possible via the untyped base class `SyncProjection.push`), iteration refuses to yield it to string-typed consumers.

Source

Thrown at libs/core/langchain_core/language_models/chat_model_stream.py:311

        """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)
        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."""

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Find the producer that inserted the non-string and fix it to push strings only (use the typed `SyncTextProjection.push`, which validates on entry — errorIndex 73's guard).
  2. Do not bypass the typed API: never call `SyncProjection.push` directly on a text projection.
  3. If mixed value types are legitimate, iterate the generic projection and stringify per item yourself.

Example fix

# before: bypassing the typed push
projection.SyncProjection.push(chunk)  # non-str enters buffer
for delta in projection: ...  # TypeError here

# after
projection.push(chunk.text or "")
for delta in projection: ...
Defensive patterns

Strategy: type-guard

Validate before calling

# Validate buffer contents before iterating (detects bad producers early)
for d in list(projection._deltas):
    assert isinstance(d, str), f"non-string delta in buffer: {d!r}"

Type guard

def text_projection_is_clean(proj) -> bool:
    """True when every buffered delta is a str (safe to iterate)."""
    return all(isinstance(d, str) for d in proj._deltas)

Try / catch

null

Prevention

When it happens

Trigger: Calling `super().push(non_str)` on the base `SyncProjection` (bypassing the typed override), then iterating the projection; or sharing the underlying buffer between a generic and a text projection where one producer pushed a non-string.

Common situations: Subclassing `SyncTextProjection` and overriding `push` without the isinstance check; adapter code that mixes base-class pushes with text-projection iteration; corrupted producer state after an upstream exception handler pushes a sentinel object.

Related errors


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