dotnet/orleans · warning · RequestFailedException

Resource not found

Error message

Resource not found

What it means

Thrown by AzureTableDataManager.DeleteTableEntryAsync when Table.DeleteEntityAsync returns HTTP 404 — the entity did not exist at delete time. The code synthesizes an Azure.RequestFailedException(status 404, 'Resource not found') so callers see a consistent exception for a missing resource. Semantically this is an idempotent-delete miss: the row is already gone.

Source

Thrown at src/Azure/Shared/Storage/AzureTableDataManager.cs:355

        /// Fails if the data does not already exist or if eTag does not match.
        /// </summary>
        /// <param name="data">Data entry to be deleted from the table.</param>
        /// <param name="eTag">ETag to use.</param>
        /// <returns>Completion promise for this storage operation.</returns>
        public async Task DeleteTableEntryAsync(T data, ETag eTag)
        {
            const string operation = "DeleteTableEntryAsync";
            var startTime = DateTime.UtcNow;
            LogTraceTableEntry(Logger, operation, data, TableName);
            try
            {
                data.ETag = eTag;
                try
                {
                    var response = await Table.DeleteEntityAsync(data.PartitionKey, data.RowKey, data.ETag);
                    if (response is { Status: 404 })
                    {
                        throw new RequestFailedException(response.Status, "Resource not found", response.ReasonPhrase, null);
                    }
                }
                catch (Exception exc)
                {
                    LogWarningDeleteTableEntry(Logger, exc, data, TableName);
                    throw;
                }
            }
            finally
            {
                CheckAlertSlowAccess(startTime, operation);
            }
        }

        /// <summary>
        /// Read a single table entry from the storage table.
        /// </summary>
        /// <param name="partitionKey">The partition key for the entry.</param>

View on GitHub (pinned to fca799fa70)

Solutions

  1. Treat 404 on delete as success (idempotent) by catching RequestFailedException where Status == 404.
  2. If strict existence is required, read-before-delete and handle the not-found case explicitly.
  3. Coordinate concurrent deleters so they do not target the same partition/row key.

Example fix

// before
try { await manager.DeleteTableEntryAsync(entity, etag); }
catch (Exception) { throw; } // 404 propagates and fails the caller
// after
try { await manager.DeleteTableEntryAsync(entity, etag); }
catch (RequestFailedException ex) when (ex.Status == 404) { /* already deleted: idempotent success */ }
Defensive patterns

Strategy: try-catch

Type guard

static bool IsNotFound(RequestFailedException ex) => ex.Status == 404;

Try / catch

try { await manager.DeleteTableEntryAsync(entity, etag); }
catch (RequestFailedException ex) when (ex.Status == 404) { /* already deleted: treat as success */ }

Prevention

When it happens

Trigger: Deleting a table entity that was already deleted, expired, or never inserted; concurrent deleters racing on the same partition/row key; replay after a prior successful delete.

Common situations: Grain deactivation racing with another deleter; eventual-consistency replay; cleanup logic running twice; reminders/storage cleanup after data was purged out-of-band.

Related errors


AI-assisted analysis of dotnet/orleans@fca799fa70 (2026-08-13). Data as JSON: /api/errors/06a6117c5d266d3a. Report an issue: GitHub.