AI agents: use the documentation index at llms.txt to locate machine-readable pages. This section is indexed by https://docs.particular.net/nservicebus/llms.txt. The markdown version of this page is served as plain text. An MCP server at /mcp serves the same content via the search_docs and read_doc tools; it is read-only and needs no credentials. Markdown versions of documentation pages are available by appending .md to the page URL. Directory URLs use index.md. They are served as text/plain because some retrieval backends reject text/markdown.

Recoverability pipeline

Component:
NServiceBus
NuGet Package:
NServiceBus 10.x

The recoverability pipeline allows for advanced customization of the metadata captured during message failures as well as full control over the recoverability action taken.

The example below shows how to extend the pipeline with a behavior that:

  • Stores the message body in an external storage
  • Excludes the body from the message sent to the error queue
  • Adds a metadata entry that links to the stored body
public class EnableExternalBodyStorageBehavior : Behavior<IRecoverabilityContext>
{
    private readonly IExternalBodyStorage storage;

    public EnableExternalBodyStorageBehavior(IExternalBodyStorage storage)
    {
        this.storage = storage;
    }

    public async override Task Invoke(IRecoverabilityContext context, Func<Task> next)
    {
        if (context.RecoverabilityAction is MoveToError errorAction)
        {
            var bodyUrl = await storage.StoreBody(context.MessageId, context.Body);

            context.Metadata["body-url"] = bodyUrl;

            context.RecoverabilityAction = new SkipFailedMessageBody(errorAction.ErrorQueue);
        }

        await next();
    }

    class SkipFailedMessageBody : MoveToError
    {
        public SkipFailedMessageBody(string errorQueue) : base(errorQueue)
        {
        }

        public override IReadOnlyCollection<IRoutingContext> GetRoutingContexts(IRecoverabilityActionContext context)
        {
            var routingContexts = base.GetRoutingContexts(context);

            foreach (var routingContext in routingContexts)
            {
                // clear out the message body
                routingContext.Message.UpdateBody(ReadOnlyMemory<byte>.Empty);
            }

            return routingContexts;
        }
    }
}

In addition, the behavior must be registered in the pipeline.