dotnet/orleans · error · ArgumentNullException

ReceiptHandle

Error message

ReceiptHandle

What it means

Thrown by SQSStorage.DeleteMessage when message.ReceiptHandle is null/empty/whitespace. SQS deletion requires a valid ReceiptHandle (issued when the message was received); without it AWS cannot identify which reception to acknowledge, so the provider rejects the call before contacting SQS.

Source

Thrown at src/AWS/Orleans.Streaming.SQS/Storage/SQSStorage.cs:219

                ReportErrorAndRethrow(exc, "GetMessages");
            }
            throw new InvalidOperationException("Unable to retrieve messages from the queue.");
        }

        /// <summary>
        /// Delete a message from SQS queue
        /// </summary>
        /// <param name="message">The message to be deleted</param>
        /// <returns></returns>
        public async Task DeleteMessage(SQSMessage message)
        {
            try
            {
                if (message == null)
                    throw new ArgumentNullException(nameof(message));

                if (string.IsNullOrWhiteSpace(message.ReceiptHandle))
                    throw new ArgumentNullException(nameof(message.ReceiptHandle));

                if (string.IsNullOrWhiteSpace(queueUrl))
                    throw new InvalidOperationException("Queue not initialized");

                await sqsClient.DeleteMessageAsync(
                    new DeleteMessageRequest { QueueUrl = queueUrl, ReceiptHandle = message.ReceiptHandle });
            }
            catch (Exception exc)
            {
                ReportErrorAndRethrow(exc, "DeleteMessage");
            }
        }

        private void ReportErrorAndRethrow(Exception exc, string operation)
        {
            LogErrorSQSOperation(exc, operation, QueueName);
            throw new AggregateException($"Error doing {operation} for SQS queue {QueueName}", exc);
        }

View on GitHub (pinned to fca799fa70)

Solutions

  1. Only delete messages freshly obtained from GetMessages so ReceiptHandle is populated.
  2. Delete within the SQS visibility timeout before the handle becomes invalid.
  3. If you build SQSMessage manually, set ReceiptHandle from a valid receive operation.
  4. Add a guard: if (string.IsNullOrWhiteSpace(msg.ReceiptHandle)) skip and log.

Example fix

// before
var msg = new SQSMessage { Body = payload }; // no ReceiptHandle
await storage.DeleteMessage(msg); // throws

// after
var received = (await storage.GetMessages(1)).First(); // has ReceiptHandle
await storage.DeleteMessage(received);
Defensive patterns

Strategy: validation

Validate before calling

if (message is null || string.IsNullOrWhiteSpace(message.ReceiptHandle))
{
    logger.LogWarning("Cannot delete SQS message: missing ReceiptHandle.");
    return;
}
await storage.DeleteMessage(message);

Type guard

static bool HasReceiptHandle(SQSMessage m) => !string.IsNullOrWhiteSpace(m?.ReceiptHandle);

Try / catch

try { await storage.DeleteMessage(message); }
catch (ArgumentNullException ex) when (ex.ParamName == "ReceiptHandle")
{
    logger.LogWarning("Delete skipped: SQS message has no ReceiptHandle.");
}

Prevention

When it happens

Trigger: Deleting an SQSMessage that was constructed manually without a ReceiptHandle, deleting a message whose ReceiptHandle expired, or deleting an object populated from a partial/older payload where ReceiptHandle was never set.

Common situations: Reusing a message object across the visibility-timeout boundary after the handle lapsed; deserializing a stored message without its handle; test fixtures that build SQSMessage instances by hand.

Related errors


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