langchain-ai/langchain · error · TypeError

AsyncTextProjection requires a string final value

Error message

AsyncTextProjection requires a string final value

What it means

`TypeError` from `AsyncTextProjection.complete`: the producer's final value is not a `str`. `complete` closes the async projection and wakes all awaiters (`await projection.text`); a non-string final value would flow into every consumer, so it is rejected at the source.

Source

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


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):
            msg = "AsyncTextProjection requires a string final value"
            raise TypeError(msg)
        super().complete(final_value)

    def __aiter__(self) -> _AsyncTextProjectionIterator:
        """Return an async iterator over text deltas."""
        return _AsyncTextProjectionIterator(self)

    def __await__(self) -> Generator[Any, None, str]:
        """Await the full accumulated text."""
        return self._await_text_impl().__await__()

    async def _await_text_impl(self) -> str:
        """Return accumulated text or raise if the final value is not a string."""
        value = await self._await_impl()
        if value is None:
            return ""
        if not isinstance(value, str):
            msg = "AsyncTextProjection received a non-string final value"
            raise TypeError(msg)

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Always complete with a string; use `complete("")` for empty streams.
  2. Convert at the completion site: `projection.complete(msg.content if isinstance(msg.content, str) else "")`.
  3. Ensure every termination path (success, error, cancellation) in the producer completes with a str or leaves the projection incomplete.

Example fix

# before
projection.complete(final_message)  # not a 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 an AsyncTextProjection final value."""
    return isinstance(v, str)

Try / catch

null

Prevention

When it happens

Trigger: Adapter code calling `projection.complete(value)` with a message object, `None`, or bytes instead of the final text — e.g. `complete(final_message)` or `complete(None)` for an empty stream — inside a custom async chat model integration.

Common situations: Completing `.text` with `AIMessage` instead of `AIMessage.content`/`.text`; empty-stream paths completing with `None`; exception handlers completing projections with sentinel objects.

Related errors


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