HangfireIO/Hangfire · error · ArgumentNullException

execution

Error message

execution

What it means

Thrown as ArgumentNullException by the BackgroundDispatcher constructor when the 'execution' IBackgroundExecution argument is null. The execution object owns the work queue and scheduling that the dispatcher's threads consume; a null execution leaves the dispatch loop with nothing to run, so the constructor rejects it via '?? throw' at line 47.

Source

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

{
    internal sealed class BackgroundDispatcher : IBackgroundDispatcher
    {
        private readonly ILog _logger = LogProvider.GetLogger(typeof(BackgroundDispatcher));
        private readonly CountdownEvent _stopped;

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

        public BackgroundDispatcher(
            [NotNull] IBackgroundExecution execution,
            [NotNull] Action<Guid, object> action,
            [CanBeNull] object state,
            [NotNull] Func<ThreadStart, IEnumerable<Thread>> threadFactory)
        {
            if (threadFactory == null) throw new ArgumentNullException(nameof(threadFactory));

            _execution = execution ?? throw new ArgumentNullException(nameof(execution));
            _action = action ?? throw new ArgumentNullException(nameof(action));
            _state = state;

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

            var threads = threadFactory(DispatchLoop)?.ToArray();

            if (threads == null || threads.Length == 0)
            {
                throw new ArgumentException("At least one unstarted thread should be created.", nameof(threadFactory));
            }

            if (threads.Any(static thread => thread == null || (thread.ThreadState & ThreadState.Unstarted) == 0))
            {
                throw new ArgumentException("All the threads should be non-null and in the ThreadState.Unstarted state.", nameof(threadFactory));
            }

View on GitHub (pinned to c236dd0f93)

Solutions

  1. If you maintain a custom processing server, ensure a non-null IBackgroundExecution is created and passed (the framework normally builds one for the configured worker count).
  2. Use the public BackgroundJobServer API, which constructs the execution and dispatcher internally.
  3. In tests, provide a real or mock IBackgroundExecution rather than null.

Example fix

// before
var d = new BackgroundDispatcher(null, action, state, factory);

// after
var execution = new BackgroundExecution(serverOptions, state);
var d = new BackgroundDispatcher(execution, action, state, factory);
Defensive patterns

Strategy: validation

Validate before calling

if (execution == null) throw new ArgumentNullException(nameof(execution));
var execution = new BackgroundExecution(serverOptions, state);

Prevention

When it happens

Trigger: Constructing a BackgroundDispatcher with a null IBackgroundExecution. BackgroundDispatcher is internal and is normally built by the processing server with a configured execution; a null execution only arises from custom processing-server code or tests that build the dispatcher manually.

Common situations: A custom processing server that instantiates BackgroundDispatcher without an IBackgroundExecution; a test stub that omits the execution; a refactor where the execution was replaced by null. Standard BackgroundJobServer users never pass this argument.

Related errors


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