Skip to content

BuiltInDeadletteringErrorHandler can throw an exception if replaying a dead letter message #111

@mhuber-evo

Description

@mhuber-evo

Hi, I've noticed that sometimes when I replay previously dead-lettered messages, an exception is thrown from Rebus, preventing further messages from being processed.

It looks like BuiltInDeadletteringErrorHandler sends the existing message headers along with a new error message and description, however if that message already had error headers, then there can be a conflict in error messages, which Azure's library will throw an exception for.

Exception that's being thrown:

System.InvalidOperationException: Differing deadletter reasons cannot be specified for both the 'propertiesToModify' and 'deadLetterReason' parameters. The values should either be identical or only be specified in one of the parameters.
   at Azure.Messaging.ServiceBus.ServiceBusReceiver.DeadLetterMessageAsync(ServiceBusReceivedMessage message, IDictionary`2 propertiesToModify, String deadLetterReason, String deadLetterErrorDescription, CancellationToken cancellationToken)
   at Rebus.Config.AdditionalAzureServiceBusConfigurationExtensions.BuiltInDeadletteringErrorHandler.HandlePoisonMessage(TransportMessage transportMessage, ITransactionContext transactionContext, ExceptionInfo exception)
   at Rebus.Retry.Simple.DefaultRetryStep.PassToErrorHandler(StepContext context, ExceptionInfo exception)
   at Rebus.Retry.Simple.DefaultRetryStep.HandleException(Exception exception, ITransactionContext transactionContext, String messageId, IncomingStepContext context, Func`1 next)        
   at Rebus.Retry.Simple.DefaultRetryStep.Process(IncomingStepContext context, Func`1 next)
   at Rebus.Workers.ThreadPoolBased.ThreadPoolWorker.ProcessMessage(TransactionContext context, TransportMessage transportMessage)

I think a straightforward way to prevent this in BuiltInDeadletteringErrorHandler would be to remove existing error headers if they exist. I can create a PR if that seems like a good solution.

transportMessage.Headers.Remove("DeadLetterReason");
transportMessage.Headers.Remove("DeadLetterErrorDescription");

_log.Error("Dead-lettering message with ID {messageId}, reason={deadLetterReason}, exception info: {exceptionInfo}",
    messageId, deadLetterReason, exception);
await messageReceiver.DeadLetterMessageAsync(message, transportMessage.Headers.ToDictionary(k => k.Key, v => (object)v.Value), deadLetterReason, deadLetterErrorDescription);

Thanks!

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions