run-llama/llama_index · error · ValueError

achat_stream is None!

Error message

achat_stream is None!

What it means

When a StreamingAgentChatResponse has is_writing_to_memory=False, its async_response_gen coroutine iterates the raw async LLM stream (self.achat_stream) instead of the internal asyncio queue. achat_stream defaults to None, so awaiting/iterating async_response_gen in that state means no async stream was ever attached to the object.

Source

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

                        try:
                            delta = await asyncio.wait_for(
                                self.aqueue.get(), timeout=0.1
                            )
                        except asyncio.TimeoutError:
                            # Break only when the stream is done and the queue is empty
                            if self.is_done and self.aqueue.empty():
                                break
                            continue
                        if delta is not None:
                            self.unformatted_response += delta
                            yield delta
                            yielded_once = True
                    else:
                        break
            else:
                if self.achat_stream is None:
                    raise ValueError("achat_stream is None!")

                async for chat_response in self.achat_stream:
                    self.unformatted_response += chat_response.delta or ""
                    yield chat_response.delta or ""
                    yielded_once = True
            self.response = self.unformatted_response.strip()

            # edge case where the stream was exhausted before yielding anything
            if not yielded_once:
                yield self.response
        finally:
            if self.awrite_response_to_history_task:
                # Make sure that the background task ran to completion, retrieve any exceptions
                await self.awrite_response_to_history_task
                self.awrite_response_to_history_task = (
                    None  # No need to keep the reference to the finished task
                )

View on GitHub (pinned to afd0fef371)

Solutions

  1. Obtain the response with await agent.astream_chat(...) so achat_stream is set, then iterate async_response_gen
  2. For sync responses use `for token in response.response_gen`
  3. When constructing manually, pass achat_stream=llm.astream_chat(...) or leave is_writing_to_memory=True so the aqueue path is used

Example fix

// before
response = agent.stream_chat("hi")
async for token in response.async_response_gen():  # ValueError: achat_stream is None!
    print(token)

// after
response = await agent.astream_chat("hi")
async for token in response.async_response_gen():
    print(token)
Defensive patterns

Strategy: validation

Validate before calling

if not response.is_writing_to_memory and response.achat_stream is None:
    raise RuntimeError("no aqueue path and no achat_stream; cannot async-iterate")

Type guard

def can_async_iterate(resp) -> bool:
    return resp.is_writing_to_memory or resp.achat_stream is not None

Try / catch

try:
    async for token in response.async_response_gen():
        print(token, end="")
except ValueError as e:
    if "achat_stream is None" in str(e):
        for token in response.response_gen:
            print(token, end="")
    else:
        raise

Prevention

When it happens

Trigger: Iterating `async for token in response.async_response_gen()` when is_writing_to_memory=False and achat_stream is None — e.g. the response came from sync stream_chat(), or the object was constructed manually without achat_stream.

Common situations: Mixing a sync chat call inside an async web handler and then trying to async-stream it; custom agents that flip is_writing_to_memory=False and forget to attach achat_stream.

Related errors


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