Search code examples
c#.netmessagingrebus

How to forward a message with exceptions to Rebus error queue


Using second-level retries in Rebus (https://github.com/rebus-org/Rebus/wiki/Automatic-retries-and-error-handling) I need to forward a message to an error queue after n retries.

This works:

_bus.Advanced.Routing.Send("my-queue.error",failedMessage.Message, failedMessage.Message);

But the exceptions accumulated in the failed message is not brought along, making the failed message in the error queue rather useless.

Ideally I would hook into the ITransport instance, and do something like this

await _transport.Send(errorQueueAddress, transportMessage, transactionContext);

(from PoisonQueueErrorHandler: https://github.com/rebus-org/Rebus/blob/333dbedf486acb92bd6c6250755537032c6215fd/Rebus/Retry/PoisonQueues/PoisonQueueErrorHandler.cs)

But there is no apparent way to get to that instance.

Any ideas on how to achieve this?


Solution

  • One way of forwarding the message to the error queue from your second level handler, is to simply throw exceptions – the usual n retries applies to 2nd level retries too, so if everything keeps failing, the message will eventually end up in the error queue.

    If you're using Rebus version 6.2.1 or later, there's an extension method on ITransportMessageApi that enables you do manually dead-letter the message when you feel like it like this:

    await bus.Advanced.TransportMessage.Deadletter("whatever error details you'd like to include!");
    

    If you're on Rebus 6.7.0 or later, the Deadletter method has an overload that accepts an exception too, which lends itself nicely to do stuff like this:

    try
    {
        // try something that can fail
    }
    catch (Exception exception)
    {
        await bus.Advanced.TransportMessage.Deadletter(exception);
    }
    

    The following answer is relevant for Rebus versions BELOW 6.2.1:

    If you really want to emulate Rebus' behavior when forwarding the message to the error queue, you can get the full error details as they would have been included by Rebus via the ErrorDescription property on IFailed<YourMessage>.

    You can then create these headers when forwarding the message:

    var details = failedMessage.ErrorDescription;
    
    var headers = new Dictionary<string, string>
    {
        {Headers.SourceQueue, "your-input-queue"},
        {Headers.ErrorDetails, details}
    };
    

    and then you should forward the transport message to the error queue, thus preserving message identity and all other headers completely intact:

    await bus.Advanced.TransportMessage.Forward("error", headers);
    

    I think this would be the most realistic emulation of Rebus' behavior.