MassTransit/MassTransit · error · ArgumentException

The configurator must be a consumer specification

Error message

The configurator must be a consumer specification

What it means

BatchConsumerConfigured on MessageScopeConfigurationObserver expects the configurator passed by the pipe to also implement IConsumerMessageSpecification<TConsumer, Batch<TMessage>> so scope filters can be added. This defensive cast failure indicates the observer was attached to a configurator type that does not implement the expected specification interface - an internal contract violation, normally caused by a custom configurator, a mismatched observer registration, or a MassTransit/container package version mismatch.

Solutions

  1. Align all MassTransit package versions (MassTransit and MassTransit.Abstractions) to the same release
  2. Register the observer only via the standard AddMassTransit configuration path, not manually
  3. Check any custom IConsumerMessageConfigurator implementations implement the matching IConsumerMessageSpecification interface
  4. Report as a bug if it occurs with stock configurators on aligned versions

Example fix

// before
observer.BatchConsumerConfigured<TConsumer, TMessage>(customConfigurator); // customConfigurator lacks IConsumerMessageSpecification
// after
var consumerSpecification = customConfigurator as IConsumerMessageSpecification<TConsumer, Batch<TMessage>>;
if (consumerSpecification == null) throw new ArgumentException("Custom configurator must implement IConsumerMessageSpecification");
Defensive patterns

Strategy: try-catch

Validate before calling

var consumerSpecification = configurator as IConsumerMessageSpecification<TConsumer, Batch<TMessage>>;
if (consumerSpecification == null) throw new InvalidOperationException("Attach MessageScopeConfigurationObserver only to standard MassTransit consumer configurators");

Type guard

static bool IsConsumerMessageSpecification<TConsumer,TMessage>(IConsumerMessageConfigurator<TConsumer, Batch<TMessage>> c) => c is IConsumerMessageSpecification<TConsumer, Batch<TMessage>>;

Try / catch

try { observer.BatchConsumerConfigured<TConsumer, TMessage>(configurator); } catch (ArgumentException ex) when (ex.Message == "The configurator must be a consumer specification") { /* log; indicates version mismatch or custom configurator bug */ }

Prevention

When it happens

Trigger: Attaching MessageScopeConfigurationObserver (directly or via AddMassTransit scoped-consume wiring) while a custom or third-party IConsumerMessageConfigurator for Batch<TMessage> does not implement IConsumerMessageSpecification<TConsumer, Batch<TMessage>>; registering batch consumers with mismatched MassTransit.Abstractions/MassTransit versions.

Common situations: Mixed NuGet package versions (MassTransit vs MassTransit.Abstractions) during an upgrade; custom consumer configurators that violate the interface contract; manually adding the observer in an unusual configuration rather than via standard AddMassTransit.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of MassTransit/MassTransit@62ab339afa (2026-09-13). Data as JSON: /api/errors/0132fa91b6211704. Report an issue: GitHub.

Appendix: source

Thrown at src/MassTransit/DependencyInjection/Configuration/MessageScopeConfigurationObserver.cs:43

            _setScopedConsumeContext = setScopedConsumeContext;

            Connect(this);
        }

        public void MessageConfigured<TMessage>(IConsumePipeConfigurator configurator)
            where TMessage : class
        {
            var scopeProvider = new ConsumeScopeProvider(_serviceProvider, _setScopedConsumeContext);
            var scopeFilter = new ScopeMessageFilter<TMessage>(scopeProvider);
            var specification = new FilterPipeSpecification<ConsumeContext<TMessage>>(scopeFilter);

            configurator.AddPipeSpecification(specification);
        }

        public override void BatchConsumerConfigured<TConsumer, TMessage>(IConsumerMessageConfigurator<TConsumer, Batch<TMessage>> configurator)
        {
            if (!(configurator is IConsumerMessageSpecification<TConsumer, Batch<TMessage>> consumerSpecification))
                throw new ArgumentException("The configurator must be a consumer specification");

            var scopeProvider = new ConsumeScopeProvider(_serviceProvider, _setScopedConsumeContext);
            var scopeFilter = new ScopeMessageFilter<Batch<TMessage>>(scopeProvider);
            var specification = new FilterPipeSpecification<ConsumeContext<Batch<TMessage>>>(scopeFilter);

            consumerSpecification.AddPipeSpecification(specification);
        }

        public override void ActivityConfigured<TActivity, TArguments>(IExecuteActivityConfigurator<TActivity, TArguments> configurator, Uri compensateAddress)
        {
            var scopeProvider = new ExecuteActivityScopeProvider<TActivity, TArguments>(_serviceProvider, _setScopedConsumeContext);
            var scopeFilter = new ScopeExecuteFilter<TActivity, TArguments>(scopeProvider);
            var specification = new FilterPipeSpecification<ExecuteContext<TArguments>>(scopeFilter);

            configurator.Arguments(x => x.AddPipeSpecification(specification));
        }

        public override void ExecuteActivityConfigured<TActivity, TArguments>(IExecuteActivityConfigurator<TActivity, TArguments> configurator)

View on GitHub (pinned to 62ab339afa)