microsoft/semantic-kernel · error · ValueError

The thread has been deleted.

Error message

The thread has been deleted.

What it means

Raised as a plain ValueError by AssistantAgentThread.get_messages() after the thread's _delete() already ran. The thread object tracks an internal _is_deleted flag; once true, the OpenAI thread id is gone server-side so listing messages has no valid target. Calling get_messages on a deleted thread is a state-machine violation, not a network failure.

Source

Thrown at python/semantic_kernel/agents/open_ai/openai_assistant_agent.py:219

        if (
            not new_message.metadata
            or "thread_id" not in new_message.metadata
            or new_message.metadata["thread_id"] != self._id
        ):
            assert self._id is not None  # nosec
            await AssistantThreadActions.create_message(self._client, self._id, new_message)

    async def get_messages(self, sort_order: Literal["asc", "desc"] | None = None) -> AsyncIterable[ChatMessageContent]:
        """Get the messages in the thread.

        Args:
            sort_order: The order to sort the messages in. Either "asc" or "desc".

        Yields:
            An AsyncIterable of ChatMessageContent of the messages in the thread.
        """
        if self._is_deleted:
            raise ValueError("The thread has been deleted.")
        if self._id is None:
            await self.create()
        assert self.id is not None  # nosec
        async for message in AssistantThreadActions.get_messages(self._client, self.id, sort_order=sort_order):
            yield message


@release_candidate
@register_agent_type("openai_assistant")
class OpenAIAssistantAgent(DeclarativeSpecMixin, Agent):
    """OpenAI Assistant Agent class.

    Provides the ability to interact with OpenAI Assistants.
    """

    # region Agent Initialization

    client: AsyncOpenAI

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. Do not call get_messages() after delete(); create a fresh AssistantAgentThread for a new conversation.
  2. Guard the call with the thread's lifecycle state or recreate the thread before reading.
  3. Ensure no concurrent coroutines delete the thread while another reads it.

Example fix

// before
await thread.delete()
msgs = [m async for m in thread.get_messages()]

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

Strategy: validation

Validate before calling

if getattr(thread, '_is_deleted', False):
    raise RuntimeError('thread already deleted; create a new one')
messages = [m async for m in thread.get_messages()]

Type guard

def thread_is_alive(thread: AssistantAgentThread) -> bool:
    return not getattr(thread, '_is_deleted', False)

Try / catch

try:
    msgs = [m async for m in thread.get_messages()]
except ValueError as e:
    if 'deleted' in str(e):
        thread = AssistantAgentThread(client=client)
    else:
        raise

Prevention

When it happens

Trigger: You call await thread.delete() (or _delete()) and then later call await thread.get_messages(...) on the same AssistantAgentThread instance. Also occurs if delete() ran as part of chat teardown and a follow-up component still holds the old thread reference and queries messages.

Common situations: Reusing a thread variable across conversation turns in a long-lived agent; cleanup hooks that delete the thread while a parallel coroutine is still reading history; agent-chat finishers that delete then log messages.

Related errors


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