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
- Do not access thread.id after delete(); drop the reference and build a fresh thread instance.
- Track deletion in your own code (a flag or None-ing the reference) so you never read a deleted thread.
- Order your logic so all reads happen before delete().
- 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
- Read thread.id only before delete().
- Null out references after deletion.
- Track your own 'deleted' flag in long-lived holders.
- Allocate a fresh thread for new conversations.
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
- Cannot create thread because it has already been deleted.
- This thread has been deleted and cannot be used anymore.
- This thread has been deleted and cannot be recreated.
- This thread cannot be deleted, since it has not been created
- This thread has been deleted and cannot be used anymore.
AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13).
Data as JSON: /api/errors/2b6ecf881b9b1f73.
Report an issue: GitHub.