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
- Obtain the response with await agent.astream_chat(...) so achat_stream is set, then iterate async_response_gen
- For sync responses use `for token in response.response_gen`
- 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
- Use async generators exclusively with responses from astream_chat
- Do not flip is_writing_to_memory without attaching the raw achat_stream
- Test custom agents with both sync and async entry points to catch mismatches early
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
- achat_stream is None. Cannot asynchronously write to history
- chat_stream is None. Cannot write to history without chat_st
- chat_stream is None!
- Streaming is not enabled. Please use achat() instead.
- Streaming is not enabled. Please use chat() instead.
AI-assisted analysis of run-llama/llama_index@afd0fef371 (2026-08-15).
Data as JSON: /api/errors/c6351861b0c2c2b8.
Report an issue: GitHub.