microsoft/semantic-kernel · error · AgentThreadOperationException

The thread could not be deleted due to an error response fro

Error message

The thread could not be deleted due to an error response from the service.

What it means

DeleteInternalAsync wraps DeleteThreadAsync. A 404 ClientResultException is intentionally swallowed (thread already gone), but any other ClientResultException (auth, rate limit, server error) is rethrown as AgentThreadOperationException.

Source

Thrown at dotnet/src/Agents/OpenAI/OpenAIAssistantAgentThread.cs:144

        }
    }

    /// <inheritdoc />
    protected override async Task DeleteInternalAsync(CancellationToken cancellationToken)
    {
        const string ErrorMessage = "The thread could not be deleted due to an error response from the service.";

        try
        {
            await this._client.DeleteThreadAsync(this.Id, cancellationToken).ConfigureAwait(false);
        }
        catch (ClientResultException ex) when (ex.Status == 404)
        {
            // Do nothing, since the thread was already deleted.
        }
        catch (ClientResultException ex)
        {
            throw new AgentThreadOperationException(ErrorMessage, ex);
        }
        catch (AggregateException ex)
        {
            throw new AgentThreadOperationException(ErrorMessage, ex);
        }
    }

    /// <inheritdoc />
    protected override async Task OnNewMessageInternalAsync(ChatMessageContent newMessage, CancellationToken cancellationToken = default)
    {
        const string ErrorMessage = "The message could not be added to the thread due to an error response from the service.";

        // If the message was generated by this agent, it is already in the thread and we shouldn't add it again.
        if (newMessage.Metadata == null || !newMessage.Metadata.TryGetValue("ThreadId", out var messageThreadId) || !string.Equals(messageThreadId, this.Id))
        {
            try
            {
                await AssistantThreadActions.CreateMessageAsync(this._client, this.Id!, newMessage, cancellationToken).ConfigureAwait(false);

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. Catch AgentThreadOperationException and inspect the ClientResultException.Status from the inner exception.
  2. Retry transient (429/5xx) deletions with backoff.
  3. Refresh credentials if Status is 401/403.
  4. Treat cleanup errors as non-fatal where appropriate, since the thread may already be gone.

Example fix

// before
await thread.DeleteAsync();
// after
try { await thread.DeleteAsync(); }
catch (AgentThreadOperationException ex) when (ex.InnerException is ClientResultException cre && (cre.Status == 429 || cre.Status >= 500)) {
    await Task.Delay(backoff);
    await thread.DeleteAsync();
}
Defensive patterns

Strategy: try-catch

Try / catch

try { await thread.DeleteAsync(); }
catch (AgentThreadOperationException ex) when (ex.InnerException is ClientResultException cre && (cre.Status == 429 || cre.Status >= 500)) {
    await Task.Delay(backoff);
    await thread.DeleteAsync();
}

Prevention

When it happens

Trigger: Deleting a thread that exists but the service returns a non-404 error: expired auth, rate limit, 5xx, or invalid id format.

Common situations: Expired credential during cleanup; rate-limited deletion; transient server error; attempting deletion on an already-corrupted thread.

Related errors


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