microsoft/semantic-kernel · error · RuntimeError

Thread has been deleted; call `create()` to recreate it.

Error message

Thread has been deleted; call `create()` to recreate it.

What it means

AgentThread tracks a deleted flag; after delete() sets it, any subsequent read of the .id property raises RuntimeError. A deleted thread is terminal and cannot be queried. The message tells you to create() a new thread — but note the same class instance cannot be recreated (see error 694); create() also refuses on a deleted instance.

Source

Thrown at python/semantic_kernel/agents/agent.py:123

# endregion


# region AgentThread


class AgentThread(ABC):
    """Base class for agent threads."""

    def __init__(self):
        """Initialize the agent thread."""
        self._is_deleted: bool = False  # type: ignore
        self._id: str | None = None  # type: ignore

    @property
    def id(self) -> str | None:
        """Returns the ID of the current thread (if any)."""
        if self._is_deleted:
            raise RuntimeError("Thread has been deleted; call `create()` to recreate it.")
        return self._id

    async def create(self) -> str | None:
        """Starts the thread and returns the thread ID."""
        # A thread should not be recreated after it has been deleted.
        if self._is_deleted:
            raise RuntimeError("Cannot create thread because it has already been deleted.")

        # If the thread ID is already set, we're done, just return the Id.
        if self.id is not None:
            return self.id

        # Otherwise, create the thread.
        self._id = await self._create()
        return self.id

    async def delete(self) -> None:
        """Ends the current thread."""

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. Do not access thread.id after delete(); drop the reference and build a fresh thread instance.
  2. Track deletion in your own code (a flag or None-ing the reference) so you never read a deleted thread.
  3. Order your logic so all reads happen before delete().
  4. If you need a new conversation after deletion, construct a new AgentThread subclass instance and call create().

Example fix

# before
await thread.delete()
print(thread.id)  # RuntimeError
# after
await thread.delete()
thread = MyAgentThread()  # new instance
await thread.create()
print(thread.id)
Defensive patterns

Strategy: validation

Validate before calling

# Track deletion in your own code; never read .id after delete().
if getattr(thread, '_is_deleted', False):
    raise RuntimeError('thread already deleted; build a new instance')
print(thread.id)

Type guard

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

Try / catch

try:
    tid = thread.id
except RuntimeError as e:
    if 'deleted' in str(e).lower():
        thread = MyAgentThread()  # new instance
        tid = await thread.create()
    else:
        raise

Prevention

When it happens

Trigger: Reading thread.id after await thread.delete(); logging/inspecting a thread you already cleaned up; reusing a thread reference past its lifecycle.

Common situations: Cleanup-then-use ordering bugs; holding a thread reference in a long-lived object after deletion; double-processing where one path deletes and another reads.

Related errors


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