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 = False

View on GitHub (pinned to afd0fef371)

Solutions

  1. Get the response from the sync agent.stream_chat() so chat_stream is populated, and iterate its response_gen
  2. For async flows use `async for token in response.async_response_gen()` instead of the sync generator
  3. 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

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


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