HangfireIO/Hangfire · error · ArgumentNullException

performer

Error message

performer

What it means

This ArgumentNullException("performer") is thrown inside GetRequiredProcesses (called from the advanced BackgroundJobServer constructor) when performer is null but at least one of factory or stateChanger is non-null. Same all-or-nothing contract as factory/stateChanger: when any of the three is supplied, all three must be supplied. The Worker process built afterwards needs a performer, so a null performer cannot be tolerated here.

Source

Thrown at src/Hangfire.Core/BackgroundJobServer.cs:208

            [CanBeNull] IBackgroundJobPerformer performer,
            [CanBeNull] IBackgroundJobStateChanger stateChanger)
        {
            var processes = new List<IBackgroundProcessDispatcherBuilder>();
            var timeZoneResolver = _options.TimeZoneResolver ?? new DefaultTimeZoneResolver();

            if (factory == null && performer == null && stateChanger == null)
            {
                filterProvider = filterProvider ?? _options.FilterProvider ?? JobFilterProviders.Providers;
                activator = activator ?? _options.Activator ?? JobActivator.Current;

                factory = new BackgroundJobFactory(filterProvider);
                performer = new BackgroundJobPerformer(filterProvider, activator, _options.TaskScheduler);
                stateChanger = new BackgroundJobStateChanger(filterProvider);
            }
            else
            {
                if (factory == null) throw new ArgumentNullException(nameof(factory));
                if (performer == null) throw new ArgumentNullException(nameof(performer));
                if (stateChanger == null) throw new ArgumentNullException(nameof(stateChanger));
            }

            processes.Add(new Worker(_options.Queues, performer, stateChanger).UseBackgroundPool(_options.WorkerCount, _options.WorkerThreadConfigurationAction));

            if (!_options.IsLightweightServer)
            {
                processes.Add(
                    new DelayedJobScheduler(_options.SchedulePollingInterval, stateChanger)
                    {
                        TaskScheduler = _options.TaskScheduler,
                        MaxDegreeOfParallelism = _options.MaxDegreeOfParallelismForSchedulers
                    }
                    .UseBackgroundPool(1));

                processes.Add(
                    new RecurringJobScheduler(factory, _options.SchedulePollingInterval, timeZoneResolver)
                        {

View on GitHub (pinned to c236dd0f93)

Solutions

  1. Provide all three of factory, performer and stateChanger when customizing any one.
  2. Or pass all three as null to let Hangfire build them.
  3. Construct the missing performer yourself, e.g. performer = new BackgroundJobPerformer(filters, activator, opts.TaskScheduler).
  4. Move off the [Obsolete] constructor per its deprecation message.

Example fix

// before
new BackgroundJobServer(opts, storage, extra, null, null, customFactory, null, null);

// after
var filters = JobFilterProviders.Providers;
new BackgroundJobServer(opts, storage, extra,
    filters, JobActivator.Current,
    customFactory,
    new BackgroundJobPerformer(filters, JobActivator.Current, opts.TaskScheduler),
    new BackgroundJobStateChanger(filters));
Defensive patterns

Strategy: validation

Validate before calling

if (performer is null && (factory is not null || stateChanger is not null))
    throw new InvalidOperationException("Supply all three of factory, performer and stateChanger, or none.");
new BackgroundJobServer(opts, storage, extra, filters, activator, factory, performer, stateChanger);

Type guard

static bool AreServicesConsistent(IBackgroundJobFactory f, IBackgroundJobPerformer p, IBackgroundJobStateChanger s)
    => (f is null && p is null && s is null) || (f is not null && p is not null && s is not null);

Try / catch

try { new BackgroundJobServer(opts, storage, extra, filters, activator, factory, performer, stateChanger); }
catch (ArgumentNullException ex) when (ex.ParamName == "performer") { throw new InvalidOperationException("Custom services must be supplied together.", ex); }

Prevention

When it happens

Trigger: Calling the advanced constructor with a non-null factory or stateChanger but a null performer, e.g. new BackgroundJobServer(opts, storage, extra, null, null, customFactory, null, null) — performer is null while factory is set, so the else-branch throws on performer.

Common situations: Supplying a custom factory (e.g. to add filters) without also supplying a performer; partial customization; misunderstanding the all-or-nothing rule for the three services.

Related errors


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