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
- Ensure the concurrency value passed in is >= 1; clamp with `Math.Max(1, value)` at the configuration boundary.
- Validate server options (e.g., BackgroundJobServerOptions.WorkerCount) before starting the server.
- 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
- Clamp all concurrency config to a minimum of 1 at the configuration boundary.
- Treat a zero/negative WorkerCount as a configuration error, not a runtime one.
- Log the raw configured value before clamping to aid diagnosis.
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
- All the threads should be non-null and in the ThreadState.Un
- Value should be greater than or equal to TimeSpan.Zero
- value
- threadCount
- waitHandle
AI-assisted analysis of HangfireIO/Hangfire@c236dd0f93 (2026-08-13).
Data as JSON: /api/errors/9dcf9376ac15941d.
Report an issue: GitHub.