{"record":{"id":"0c86528c6fa211a3","repo":"microsoft/semantic-kernel","slug":"cannot-retrieve-chat-history-since-the-thread-has-0c8652","errorCode":null,"errorMessage":"Cannot retrieve chat history, since the thread has been deleted.","messagePattern":"Cannot retrieve chat history, since the thread has been deleted\\.","errorType":"exception","errorClass":"AgentThreadOperationException","httpStatus":null,"severity":"error","filePath":"python/semantic_kernel/agents/open_ai/openai_responses_agent.py","lineNumber":230,"sourceCode":"            raise AgentThreadOperationException(\"Cannot delete the thread, since it has not been created.\")\n        self._chat_history.clear()\n        self._is_deleted = True\n\n    @override\n    async def _on_new_message(self, new_message: str | ChatMessageContent) -> None:\n        \"\"\"Called when a new message has been contributed to the chat.\"\"\"\n        if isinstance(new_message, str):\n            new_message = ChatMessageContent(role=AuthorRole.USER, content=new_message)\n\n        if not self.response_id:\n            self._chat_history.add_message(new_message)\n\n    async def get_messages(\n        self, limit: int | None = None, sort_order: Literal[\"asc\", \"desc\"] | None = \"desc\"\n    ) -> AsyncIterable[ChatMessageContent]:\n        \"\"\"Retrieve the current chat history.\"\"\"\n        if self._is_deleted:\n            raise AgentThreadOperationException(\"Cannot retrieve chat history, since the thread has been deleted.\")\n        if self.store_enabled and self.response_id is not None:\n            async for message in ResponsesAgentThreadActions.get_messages(\n                self._client,\n                self.response_id,\n                limit=limit,\n                sort_order=sort_order,\n            ):\n                yield message\n        else:\n            for message in self._chat_history.messages:\n                yield message\n\n    async def reduce(self) -> ChatHistory | None:\n        \"\"\"Reduce the chat history to a smaller size.\"\"\"\n        if self._id is None:\n            raise AgentThreadOperationException(\"Cannot reduce chat history, since the thread is not currently active.\")\n        if not isinstance(self._chat_history, ChatHistoryReducer):\n            return None","sourceCodeStart":212,"sourceCodeEnd":248,"githubUrl":"https://github.com/microsoft/semantic-kernel/blob/c028a0c7dc4f0814cdcbaba9d998f187a41197bf/python/semantic_kernel/agents/open_ai/openai_responses_agent.py#L212-L248","documentation":"Raised by ResponsesAgentThread.get_messages() when self._is_deleted is True. The thread object remembers a deleted flag set by its delete() method, and any subsequent attempt to read back the chat history is refused. This is a guard against using stale thread state after the backing OpenAI Responses resource (or local history) has been discarded.","triggerScenarios":"Calling await thread.get_messages(...) on a ResponsesAgentThread after thread.delete() has already been awaited on that same instance. The _is_deleted flag is set during delete() and never cleared, so every subsequent get_messages() / reduce iteration hits the guard.","commonSituations":"A cleanup routine deletes the thread and then a follow-up handler (logging, audit, or a retry path) tries to read messages. Also happens when a thread object is shared across coroutines and one path deletes it while another still reads. Rewinding or reusing a thread variable after explicit deletion is the typical cause.","solutions":["Stop calling get_messages() on a thread you already deleted; capture the messages you need before calling delete().","After delete(), set the variable to None / create a new ResponsesAgentThread so downstream code cannot reach the stale instance.","If you need history after deletion, read it into a local list first, then delete, then operate on the local copy."],"exampleFix":"// before\nawait thread.delete()\nhistory = await thread.get_messages()  # raises\n\n// after\nhistory = [m async for m in thread.get_messages()]\nawait thread.delete()","handlingStrategy":"validation","validationCode":"# Before reading history, confirm the thread is not deleted\nif not getattr(thread, '_is_deleted', False):\n    messages = [m async for m in thread.get_messages()]\nelse:\n    messages = []","typeGuard":"def is_thread_usable(thread) -> bool:\n    return not getattr(thread, '_is_deleted', False) and thread is not None","tryCatchPattern":"from semantic_kernel.exceptions.agent_exceptions import AgentThreadOperationException\ntry:\n    async for msg in thread.get_messages():\n        ...\nexcept AgentThreadOperationException:\n    # thread already deleted; use previously captured history\n    pass","preventionTips":["Capture messages into a local list before calling thread.delete().","Set thread variables to None after deletion to prevent reuse.","Treat delete() as terminal — never read from a deleted thread."],"tags":["agent-thread","state-management","responses-agent"],"backgroundTag":null,"analyzedSha":"c028a0c7dc4f0814cdcbaba9d998f187a41197bf","analyzedAt":"2026-08-13T13:48:05.040Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}