microsoft/semantic-kernel · error · AgentThreadOperationException

Cannot retrieve chat history, since the thread has been dele

Error message

Cannot retrieve chat history, since the thread has been deleted.

What it means

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.

Source

Thrown at python/semantic_kernel/agents/open_ai/openai_responses_agent.py:230

            raise AgentThreadOperationException("Cannot delete the thread, since it has not been created.")
        self._chat_history.clear()
        self._is_deleted = True

    @override
    async def _on_new_message(self, new_message: str | ChatMessageContent) -> None:
        """Called when a new message has been contributed to the chat."""
        if isinstance(new_message, str):
            new_message = ChatMessageContent(role=AuthorRole.USER, content=new_message)

        if not self.response_id:
            self._chat_history.add_message(new_message)

    async def get_messages(
        self, limit: int | None = None, sort_order: Literal["asc", "desc"] | None = "desc"
    ) -> AsyncIterable[ChatMessageContent]:
        """Retrieve the current chat history."""
        if self._is_deleted:
            raise AgentThreadOperationException("Cannot retrieve chat history, since the thread has been deleted.")
        if self.store_enabled and self.response_id is not None:
            async for message in ResponsesAgentThreadActions.get_messages(
                self._client,
                self.response_id,
                limit=limit,
                sort_order=sort_order,
            ):
                yield message
        else:
            for message in self._chat_history.messages:
                yield message

    async def reduce(self) -> ChatHistory | None:
        """Reduce the chat history to a smaller size."""
        if self._id is None:
            raise AgentThreadOperationException("Cannot reduce chat history, since the thread is not currently active.")
        if not isinstance(self._chat_history, ChatHistoryReducer):
            return None

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. Stop calling get_messages() on a thread you already deleted; capture the messages you need before calling delete().
  2. After delete(), set the variable to None / create a new ResponsesAgentThread so downstream code cannot reach the stale instance.
  3. If you need history after deletion, read it into a local list first, then delete, then operate on the local copy.

Example fix

// before
await thread.delete()
history = await thread.get_messages()  # raises

// after
history = [m async for m in thread.get_messages()]
await thread.delete()
Defensive patterns

Strategy: validation

Validate before calling

# Before reading history, confirm the thread is not deleted
if not getattr(thread, '_is_deleted', False):
    messages = [m async for m in thread.get_messages()]
else:
    messages = []

Type guard

def is_thread_usable(thread) -> bool:
    return not getattr(thread, '_is_deleted', False) and thread is not None

Try / catch

from semantic_kernel.exceptions.agent_exceptions import AgentThreadOperationException
try:
    async for msg in thread.get_messages():
        ...
except AgentThreadOperationException:
    # thread already deleted; use previously captured history
    pass

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13). Data as JSON: /api/errors/0c86528c6fa211a3. Report an issue: GitHub.