{"record":{"id":"2992483dbe22003a","repo":"run-llama/llama_index","slug":"achat-stream-is-none-cannot-asynchronously-write","errorCode":null,"errorMessage":"achat_stream is None. Cannot asynchronously write to history without achat_stream.","messagePattern":"achat_stream is None\\. Cannot asynchronously write to history without achat_stream\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"llama-index-core/llama_index/core/chat_engine/types.py","lineNumber":234,"sourceCode":"\n        # This act as is_done events for any consumers waiting\n        self.is_function_not_none_thread_event.set()\n        if on_stream_end_fn is not None and not self.is_function:\n            on_stream_end_fn()\n\n    @dispatcher.span\n    async def awrite_response_to_history(\n        self,\n        memory: BaseMemory,\n        on_stream_end_fn: Optional[Callable] = None,\n    ) -> None:\n        self._ensure_async_setup()\n        assert self.aqueue is not None\n        assert self.is_function_false_event is not None\n        assert self.new_item_event is not None\n\n        if self.achat_stream is None:\n            raise ValueError(\n                \"achat_stream is None. Cannot asynchronously write to \"\n                \"history without achat_stream.\"\n            )\n\n        # try/except to prevent hanging on error\n        dispatcher.event(StreamChatStartEvent())\n        try:\n            final_text = \"\"\n            async for chat in self.achat_stream:\n                self.is_function = is_function(chat.message)\n                if chat.delta:\n                    dispatcher.event(\n                        StreamChatDeltaReceivedEvent(\n                            delta=chat.delta,\n                        )\n                    )\n                    self.aput_in_queue(chat.delta)\n                final_text += chat.delta or \"\"","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/run-llama/llama_index/blob/afd0fef371831f9bda13e5af7167cf4e981278ab/llama-index-core/llama_index/core/chat_engine/types.py#L216-L252","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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)","If constructing the object yourself, set achat_stream: StreamingAgentChatResponse(achat_stream=llm.astream_chat(...))","Prefer agent.astream_chat() end-to-end and simply iterate response.response_gen — history writing happens automatically"],"exampleFix":"// before\nresponse = agent.stream_chat(\"hi\")\nawait response.awrite_response_to_history(memory)  # ValueError: achat_stream is None\n\n// after\nresponse = await agent.astream_chat(\"hi\")\nawait response.awrite_response_to_history(memory)","handlingStrategy":"validation","validationCode":"if response.achat_stream is None:\n    raise RuntimeError(\"response has no async stream; use write_response_to_history\")","typeGuard":"from llama_index.core.chat_engine.types import StreamingAgentChatResponse\n\ndef has_async_stream(resp: StreamingAgentChatResponse) -> bool:\n    return resp.achat_stream is not None","tryCatchPattern":"try:\n    await response.awrite_response_to_history(memory)\nexcept ValueError as e:\n    if \"achat_stream is None\" in str(e):\n        response.write_response_to_history(memory)  # sync fallback\n    else:\n        raise","preventionTips":["Keep the whole request path one flavor: astream_chat + awrite_response_to_history","Lint for calls to sync agent methods inside async functions (ruff rule ASYNC / asyncio-dangling)","Never construct StreamingAgentChatResponse without the stream you intend to consume"],"tags":["streaming","chat-engine","agents","async","sync-async-mismatch"],"backgroundTag":null,"analyzedSha":"afd0fef371831f9bda13e5af7167cf4e981278ab","analyzedAt":"2026-08-15T05:42:58.429Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}