HangfireIO/Hangfire · error · ArgumentOutOfRangeException

maxConcurrency

Error message

maxConcurrency

What it means

Thrown by BackgroundDispatcherAsync's constructor (internal) when maxConcurrency is less than or equal to zero. maxConcurrency drives a CountdownEvent and a for-loop that spawns that many dispatch tasks, so a non-positive value is meaningless and would break the shutdown counter.

Source

Thrown at src/Hangfire.Core/Processing/BackgroundDispatcherAsync.cs:47

        private readonly ILog _logger = LogProvider.GetLogger(typeof(BackgroundDispatcherAsync));
        private readonly CountdownEvent _stopped;

        private readonly IBackgroundExecution _execution;
        private readonly Func<Guid, object, Task> _action;
        private readonly object _state;

        private readonly TaskScheduler _taskScheduler;
        private readonly bool _ownsScheduler;

        public BackgroundDispatcherAsync(
            [NotNull] IBackgroundExecution execution,
            [NotNull] Func<Guid, object, Task> action,
            [CanBeNull] object state,
            [NotNull] TaskScheduler taskScheduler,
            int maxConcurrency,
            bool ownsScheduler)
        {
            if (maxConcurrency <= 0) throw new ArgumentOutOfRangeException(nameof(maxConcurrency));

            _execution = execution ?? throw new ArgumentNullException(nameof(execution));
            _action = action ?? throw new ArgumentNullException(nameof(action));
            _state = state;
            _taskScheduler = taskScheduler ?? throw new ArgumentNullException(nameof(taskScheduler));
            _ownsScheduler = ownsScheduler;

#if !NETSTANDARD1_3
            AppDomainUnloadMonitor.EnsureInitialized();
#endif

            _stopped = new CountdownEvent(maxConcurrency);

            for (var i = 0; i < maxConcurrency; i++)
            {
                Task.Factory.StartNew(
                    DispatchLoop,
                    CancellationToken.None,

View on GitHub (pinned to c236dd0f93)

Solutions

  1. Ensure the concurrency value passed in is >= 1; clamp with `Math.Max(1, value)` at the configuration boundary.
  2. Validate server options (e.g., BackgroundJobServerOptions.WorkerCount) before starting the server.
  3. Check the source feeding maxConcurrency (config file, env var) for missing or empty values.

Example fix

// before
var dispatcher = new BackgroundDispatcherAsync(execution, action, state, scheduler, maxConcurrency: 0, false);

// after
var concurrency = Math.Max(1, configuredWorkerCount);
var dispatcher = new BackgroundDispatcherAsync(execution, action, state, scheduler, concurrency, false);
Defensive patterns

Strategy: validation

Validate before calling

int concurrency = Math.Max(1, configuredConcurrency);
// then pass concurrency to the dispatcher

Try / catch

try { /* construct dispatcher with concurrency */ }
catch (ArgumentOutOfRangeException ex) when (ex.ParamName == "maxConcurrency")
{ concurrency = 1; /* retry construction */ }

Prevention

When it happens

Trigger: Constructing BackgroundDispatcherAsync with maxConcurrency = 0 or negative. Reached through Hangfire server setup that computes concurrency from a misconfigured source (e.g., a zero-valued WorkerCount or a negative derived value).

Common situations: Server options set WorkerCount to 0; a configuration provider returns an unset/default int that resolves to 0; environment variable parsed as negative; dynamic scaling logic underflows to zero.

Related errors


AI-assisted analysis of HangfireIO/Hangfire@c236dd0f93 (2026-08-13). Data as JSON: /api/errors/9dcf9376ac15941d. Report an issue: GitHub.