dotnetcore/CAP · error · ArgumentNullException

Value cannot be null. (Parameter 'topics')

Error message

Value cannot be null. (Parameter 'topics')

What it means

SubscribeAsync on the Azure Service Bus consumer requires a non-null topics collection to create subscriptions/entities. Null is treated as a programming error and throws ArgumentNullException; an empty list is allowed (no-op after connect).

Solutions

  1. Pass a non-null collection; use Array.Empty<string>() for no topics
  2. Register subscribers with x.AddSubscribe<T>() so CAP builds the topic list
  3. Guard the call site against null before invoking SubscribeAsync
  4. Catch ArgumentNullException during bootstrap and log which transport failed

Example fix

// before
await asbConsumer.SubscribeAsync(topicsFromConfig); // null
// after
await asbConsumer.SubscribeAsync(topicsFromConfig ?? Enumerable.Empty<string>());
Defensive patterns

Strategy: validation

Validate before calling

if (topics is null) throw new InvalidOperationException("topics must not be null");

Type guard

bool IsValidTopics(IEnumerable<string>? t) => t is not null;

Try / catch

try { await client.SubscribeAsync(topics); } catch (ArgumentNullException ex) when (ex.ParamName == "topics") { logger.LogError(ex, "ASB SubscribeAsync got null topics"); throw; }

Prevention

When it happens

Trigger: Calling AzureServiceBusConsumerClient.SubscribeAsync(null) directly or via a custom bootstrap that forwards a null subscriber collection; can also happen in CAP's ConsistencyBootstrapper path when subscriber descriptors were never initialized.

Common situations: Custom CAP forks replacing the consumer client factory with one that passes null when there are no consumers; refactors where a topic list built from reflection/config came back null instead of empty.

Related errors


AI-assisted analysis of dotnetcore/CAP@e52b8508e5 (2026-09-14). Data as JSON: /api/errors/418d1a53e5762c10. Report an issue: GitHub.

Appendix: source

Thrown at src/DotNetCore.CAP.AzureServiceBus/AzureServiceBusConsumerClient.cs:56

        IServiceProvider serviceProvider)
    {
        _logger = logger;
        _subscriptionName = subscriptionName;
        _groupConcurrent = groupConcurrent;
        _semaphore = new SemaphoreSlim(groupConcurrent);
        _serviceProvider = serviceProvider;
        _asbOptions = options.Value ?? throw new ArgumentNullException(nameof(options));
    }

    public Func<TransportMessage, object?, Task>? OnMessageCallback { get; set; }

    public Action<LogMessageEventArgs>? OnLogCallback { get; set; }

    public BrokerAddress BrokerAddress => ServiceBusHelpers.GetBrokerAddress(_asbOptions.ConnectionString, _asbOptions.Namespace);

    public async Task SubscribeAsync(IEnumerable<string> topics)
    {
        if (topics == null) throw new ArgumentNullException(nameof(topics));

        await ConnectAsync();

        if (!_asbOptions.AutoProvision) 
            return;

        topics = topics.Concat(_asbOptions!.SQLFilters?.Select(o => o.Key) ?? []);

        var allRules = _administrationClient!.GetRulesAsync(_asbOptions!.TopicPath, _subscriptionName).ToBlockingEnumerable();
        var allRuleNames = allRules.Select(o => o.Name);

        foreach (var newRule in topics.Except(allRuleNames))
        {
            var isSqlRule = _asbOptions.SQLFilters?.FirstOrDefault(o => o.Key == newRule).Value is not null;

            RuleFilter? currentRuleToAdd = default;

            if (isSqlRule)

View on GitHub (pinned to e52b8508e5)