microsoft/semantic-kernel · error · InvalidOperationException
This thread has been deleted and cannot be used anymore.
Error message
This thread has been deleted and cannot be used anymore.
What it means
Thrown by AzureAIAgentThread.GetMessagesAsync when IsDeleted is true. This is the Azure AI-specific message retrieval method that enumerates all messages in a thread. After DeleteAsync has completed (IsDeleted = true), calling GetMessagesAsync is rejected because the underlying thread no longer exists on the service.
Source
Thrown at dotnet/src/Agents/AzureAI/AzureAIAgentThread.cs:156
{
throw new AgentThreadOperationException(ErrorMessage, ex);
}
}
}
/// <summary>
/// Asynchronously retrieves all messages in the thread.
/// </summary>
/// <param name="sortOrder">The order to return messages in.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>The messages in the thread.</returns>
/// <exception cref="InvalidOperationException">The thread has been deleted.</exception>
[Experimental("SKEXP0110")]
public async IAsyncEnumerable<ChatMessageContent> GetMessagesAsync(ListSortOrder? sortOrder = default, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
if (this.IsDeleted)
{
throw new InvalidOperationException("This thread has been deleted and cannot be used anymore.");
}
if (this.Id is null)
{
await this.CreateAsync(cancellationToken).ConfigureAwait(false);
}
await foreach (var message in AgentThreadActions.GetMessagesAsync(this._client, this.Id!, sortOrder, cancellationToken).ConfigureAwait(false))
{
yield return message;
}
}
}
View on GitHub (pinned to c028a0c7dc)
Solutions
- Retrieve messages before calling DeleteAsync.
- Guard with: if (!thread.IsDeleted) { await foreach (var m in thread.GetMessagesAsync()) {...} }
- Cache messages locally before deletion if post-deletion access is needed.
Example fix
// before
await thread.DeleteAsync(ct);
await foreach (var msg in thread.GetMessagesAsync()) { ... } // throws
// after
await foreach (var msg in thread.GetMessagesAsync()) { history.Add(msg); }
await thread.DeleteAsync(ct); Defensive patterns
Strategy: validation
Validate before calling
if (!azureThread.IsDeleted)
{
await foreach (var msg in azureThread.GetMessagesAsync(cancellationToken: ct))
{
messages.Add(msg);
}
} Try / catch
try
{
await foreach (var msg in thread.GetMessagesAsync(cancellationToken: ct))
{
history.Add(msg);
}
}
catch (InvalidOperationException ex) when (ex.Message.Contains("has been deleted"))
{
_logger.LogWarning("Cannot retrieve messages: thread was deleted.");
} Prevention
- Always retrieve messages before deletion.
- Guard every GetMessagesAsync call with an IsDeleted check.
- Cache messages locally if they are needed after cleanup.
When it happens
Trigger: Calling azureThread.GetMessagesAsync() after the thread has been deleted via DeleteAsync. The method checks IsDeleted before attempting lazy creation or message retrieval.
Common situations: Fetching message history for logging/display after the conversation cleanup has already run. Caching the thread reference and calling GetMessagesAsync in a post-session report. Race condition between a cleanup task and a history-retrieval task sharing the same thread reference.
Related errors
- This thread cannot be deleted, since it has not been created
- AIContextProviders with AIFunctions are not supported when A
- The thread could not be created due to an error response fro
- The thread could not be deleted due to an error response fro
- The message could not be added to the thread due to an error
AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13).
Data as JSON: /api/errors/a231c25d0561cf3d.
Report an issue: GitHub.