run-llama/llama_index · error · ValueError

achat_stream is None. Cannot asynchronously write to history

Error message

achat_stream is None. Cannot asynchronously write to history without achat_stream.

What it means

StreamingAgentChatResponse.awrite_response_to_history() consumes the asynchronous LLM stream (self.achat_stream) to persist the final message into memory. achat_stream defaults to None, so the method raises immediately when no async stream was ever attached — typically because the response was produced by the synchronous path or built manually.

Source

Thrown at llama-index-core/llama_index/core/chat_engine/types.py:234

        # This act as is_done events for any consumers waiting
        self.is_function_not_none_thread_event.set()
        if on_stream_end_fn is not None and not self.is_function:
            on_stream_end_fn()

    @dispatcher.span
    async def awrite_response_to_history(
        self,
        memory: BaseMemory,
        on_stream_end_fn: Optional[Callable] = None,
    ) -> None:
        self._ensure_async_setup()
        assert self.aqueue is not None
        assert self.is_function_false_event is not None
        assert self.new_item_event is not None

        if self.achat_stream is None:
            raise ValueError(
                "achat_stream is None. Cannot asynchronously write to "
                "history without achat_stream."
            )

        # try/except to prevent hanging on error
        dispatcher.event(StreamChatStartEvent())
        try:
            final_text = ""
            async for chat in self.achat_stream:
                self.is_function = is_function(chat.message)
                if chat.delta:
                    dispatcher.event(
                        StreamChatDeltaReceivedEvent(
                            delta=chat.delta,
                        )
                    )
                    self.aput_in_queue(chat.delta)
                final_text += chat.delta or ""

View on GitHub (pinned to afd0fef371)

Solutions

  1. Use the matching method: responses from stream_chat() need response.write_response_to_history(memory), responses from astream_chat() need await response.awrite_response_to_history(memory)
  2. If constructing the object yourself, set achat_stream: StreamingAgentChatResponse(achat_stream=llm.astream_chat(...))
  3. Prefer agent.astream_chat() end-to-end and simply iterate response.response_gen — history writing happens automatically

Example fix

// before
response = agent.stream_chat("hi")
await response.awrite_response_to_history(memory)  # ValueError: achat_stream is None

// after
response = await agent.astream_chat("hi")
await response.awrite_response_to_history(memory)
Defensive patterns

Strategy: validation

Validate before calling

if response.achat_stream is None:
    raise RuntimeError("response has no async stream; use write_response_to_history")

Type guard

from llama_index.core.chat_engine.types import StreamingAgentChatResponse

def has_async_stream(resp: StreamingAgentChatResponse) -> bool:
    return resp.achat_stream is not None

Try / catch

try:
    await response.awrite_response_to_history(memory)
except ValueError as e:
    if "achat_stream is None" in str(e):
        response.write_response_to_history(memory)  # sync fallback
    else:
        raise

Prevention

When it happens

Trigger: Calling await awrite_response_to_history(memory) on a response from agent.stream_chat() (sync — sets chat_stream only); calling it on a hand-constructed StreamingAgentChatResponse() without achat_stream; consuming achat_stream fully yourself and then calling the method.

Common situations: Calling sync agent methods inside async endpoints and then trying to use the async history writer; custom agent implementations that create StreamingAgentChatResponse() and forget to set achat_stream; porting sync example code to async half-way.

Related errors


AI-assisted analysis of run-llama/llama_index@afd0fef371 (2026-08-15). Data as JSON: /api/errors/2992483dbe22003a. Report an issue: GitHub.