langchain-ai/langchain · error · TypeError

AsyncTextProjection received a non-string delta

Error message

AsyncTextProjection received a non-string delta

What it means

`TypeError` from the async iterator of `AsyncTextProjection` (`async for delta in projection.text`): an item delivered from the projection's queue is not a string. It is the async counterpart of errorIndex 75 — a runtime check that catches non-string deltas that bypassed the typed `push` (e.g. via the untyped base `AsyncProjection.push`).

Source

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

                    if proj.error is not None:
                        raise proj.error
                    raise StopAsyncIteration
            else:
                proj._event.clear()  # noqa: SLF001
                await proj._event.wait()  # noqa: SLF001


class _AsyncTextProjectionIterator(_AsyncProjectionIterator):
    """Async iterator over an `AsyncTextProjection`'s text deltas."""

    __slots__ = ()

    async def __anext__(self) -> str:
        """Return the next text delta."""
        item = await super().__anext__()
        if not isinstance(item, str):
            msg = "AsyncTextProjection received a non-string delta"
            raise TypeError(msg)
        return item


class AsyncTextProjection(AsyncProjection):
    """String-specialized async projection for `.text` and `.reasoning`."""

    __slots__ = ()

    def push(self, delta: str) -> None:
        """Append a text delta and notify waiters."""
        if not isinstance(cast("Any", delta), str):
            msg = "AsyncTextProjection requires a string delta"
            raise TypeError(msg)
        super().push(delta)

    def complete(self, final_value: str) -> None:
        """Set the final accumulated text and notify waiters."""
        if not isinstance(cast("Any", final_value), str):

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Push only strings through the typed `AsyncTextProjection.push` (validates on entry — errorIndex 78's guard): `await-safe projection.push(chunk.text)`.
  2. Guard optional fields before pushing: `if chunk.text: projection.push(chunk.text)`.
  3. Remove direct base-class `push`/`complete` calls from your adapter.

Example fix

# before
projection.AsyncProjection.push(chunk)  # bypasses type check
async for delta in projection: ...  # TypeError

# after
if chunk.text:
    projection.push(chunk.text)
async for delta in projection: ...
Defensive patterns

Strategy: type-guard

Validate before calling

assert isinstance(item, str), f"async text delta must be str, got {type(item).__name__}"
# push-side validation prevents this entirely:
if isinstance(delta, str):
    projection.push(delta)

Type guard

def is_async_text_delta(v: object) -> bool:
    """True when v is a valid AsyncTextProjection delta."""
    return isinstance(v, str)

Try / catch

null

Prevention

When it happens

Trigger: Producer code calling `AsyncProjection.push(non_str)` (base class) or an overriding subclass omitting the type check, followed by `async for delta in projection:` consuming the corrupted buffer. Affects `.text` and `.reasoning` async projections in streaming chat model code.

Common situations: Custom async model adapters pushing AIMessageChunk objects or `None` into `.text`; subclasses of `AsyncTextProjection` that override `push` without validation; migration from dict-based accumulation where values were sometimes non-string.

Related errors


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