microsoft/semantic-kernel · warning · ValueError

The thread has been deleted.

Error message

The thread has been deleted.

What it means

Raised by AzureAIAgentThread.get_messages when self._is_deleted is True. After a thread is deleted it cannot serve messages, so any further read is rejected. Thrown as a plain ValueError at the start of get_messages.

Source

Thrown at python/semantic_kernel/agents/azure_ai/azure_ai_agent.py:343

        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 AgentThreadActions.create_message(self._client, self.id, new_message)

    async def get_messages(self, sort_order: Literal["asc", "desc"] = "desc") -> 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 AgentThreadActions.get_messages(self._client, self.id, sort_order=sort_order):
            yield message


@experimental
@register_agent_type("foundry_agent")
class AzureAIAgent(DeclarativeSpecMixin, Agent):
    """Azure AI Agent class."""

    client: AIProjectClient
    definition: AzureAIAgentModel
    polling_options: RunPollingOptions = Field(default_factory=RunPollingOptions)
    mcp_tool_approval_callback: MCPToolApprovalCallback | None = None

    channel_type: ClassVar[type[AgentChannel]] = AzureAIChannel

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. Do not call get_messages after delete; restructure teardown so reads happen before deletion.
  2. Track thread lifecycle in your code and skip reads when the thread is known to be deleted.
  3. Create a fresh AzureAIAgentThread for a new session instead of reusing a deleted one.

Example fix

// before
await thread.delete()
async for m in thread.get_messages(): ...  // error
// after
async for m in thread.get_messages(): ...
await thread.delete()  // reads first, then delete
Defensive patterns

Strategy: validation

Validate before calling

async def safe_get_messages(thread):
    if getattr(thread, '_is_deleted', False):
        return []
    return [m async for m in thread.get_messages()]

Type guard

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

Try / catch

try:
    async for m in thread.get_messages(): ...
except ValueError as e:
    if 'has been deleted' in str(e):
        log.warning('skipping read on deleted thread')
    else:
        raise

Prevention

When it happens

Trigger: Calling get_messages() after await thread.delete() completed; iterating history in a finally/teardown block that runs after deletion; holding a stale thread reference across a session reset.

Common situations: Cleanup ordering bug where history retrieval and deletion race; UI/test code that lists messages after the user/session teardown deleted the thread; reusing a thread object across multiple create/delete cycles without resetting the _is_deleted flag.

Related errors


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