run-llama/llama_index · error · ValueError
chat_stream is None!
Error message
chat_stream is None!
What it means
When a StreamingAgentChatResponse has is_writing_to_memory=False, its sync response_gen property iterates the raw LLM stream (self.chat_stream) instead of the internal queue. chat_stream defaults to None, so iterating response_gen in that state means no stream was ever attached — usually a sync/async mix-up or a hand-built object.
Source
Thrown at llama-index-core/llama_index/core/chat_engine/types.py:307
@property
def response_gen(self) -> Generator[str, None, None]:
try:
yielded_once = False
if self.is_writing_to_memory:
while not self.is_done or not self.queue.empty():
if self.exception is not None:
raise self.exception
try:
delta = self.queue.get(block=False)
self.unformatted_response += delta
yield delta
yielded_once = True
except Empty:
time.sleep(0)
else:
if self.chat_stream is None:
raise ValueError("chat_stream is None!")
for chat_response in self.chat_stream:
self.unformatted_response += chat_response.delta or ""
yield chat_response.delta or ""
yielded_once = True
self.response = self.unformatted_response.strip()
if not yielded_once:
yield self.response
finally:
if self.write_response_to_history_thread is not None:
self.write_response_to_history_thread.join()
self.write_response_to_history_thread = None
async def async_response_gen(self) -> AsyncGenerator[str, None]:
try:
yielded_once = FalseView on GitHub (pinned to afd0fef371)
Solutions
- Get the response from the sync agent.stream_chat() so chat_stream is populated, and iterate its response_gen
- For async flows use `async for token in response.async_response_gen()` instead of the sync generator
- If building the object manually, pass chat_stream=llm.stream_chat(...) or keep is_writing_to_memory=True so the queue path is used
Example fix
// before
response = await agent.astream_chat("hi")
for token in response.response_gen: # ValueError: chat_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.chat_stream is None:
raise RuntimeError("no queue path and no chat_stream; cannot sync-iterate") Type guard
def can_sync_iterate(resp) -> bool:
return resp.is_writing_to_memory or resp.chat_stream is not None Try / catch
try:
for token in response.response_gen:
print(token, end="")
except ValueError as e:
if "chat_stream is None" in str(e):
print(response.response)
else:
raise Prevention
- Only iterate response_gen from responses produced by the matching sync engine call
- In custom agents keep is_writing_to_memory=True unless you also attach the raw stream
- Centralize token-iteration in one utility so the sync/async contract is enforced once
When it happens
Trigger: Iterating response.response_gen on an object where is_writing_to_memory=False and chat_stream is None — e.g. a response from agent.astream_chat() consumed synchronously, or a manually constructed StreamingAgentChatResponse() with no chat_stream argument.
Common situations: Custom agent subclasses that set is_writing_to_memory=False; reusing one response object across sync and async consumers; copying example code that manipulates these flags.
Related errors
- chat_stream is None. Cannot write to history without chat_st
- achat_stream is None. Cannot asynchronously write to history
- achat_stream is None!
- Streaming is not enabled. Please use chat() instead.
- Streaming is not enabled. Please use achat() instead.
AI-assisted analysis of run-llama/llama_index@afd0fef371 (2026-08-15).
Data as JSON: /api/errors/9a7280c411212a81.
Report an issue: GitHub.