HangfireIO/Hangfire · error · ArgumentNullException
factory
Error message
factory
What it means
This ArgumentNullException("factory") is thrown inside GetRequiredProcesses (called from the advanced BackgroundJobServer constructor) when factory is null but at least one of performer or stateChanger is non-null. The code treats factory/performer/stateChanger as all-or-nothing: if all three are null it auto-creates them from filterProvider/activator; otherwise it requires all three to be provided. Passing a custom performer or stateChanger without also supplying a factory triggers this throw.
Source
Thrown at src/Hangfire.Core/BackgroundJobServer.cs:207
[CanBeNull] IBackgroundJobFactory factory,
[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
- Provide all three of factory, performer and stateChanger when you customize any one of them.
- Or pass all three as null so Hangfire auto-creates them from the filter provider / activator.
- Construct the missing pieces yourself, e.g. factory = new BackgroundJobFactory(filterProvider ?? JobFilterProviders.Providers).
- Migrate off the [Obsolete] advanced constructor by building your own BackgroundJobServer-like type as the obsolete message advises.
Example fix
// before
new BackgroundJobServer(opts, storage, extra, null, null, null, customPerformer, null);
// after
var filters = JobFilterProviders.Providers;
new BackgroundJobServer(opts, storage, extra,
filters, JobActivator.Current,
new BackgroundJobFactory(filters),
customPerformer,
new BackgroundJobStateChanger(filters)); Defensive patterns
Strategy: validation
Validate before calling
if (factory is null && (performer 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 == "factory") { throw new InvalidOperationException("Custom services must be supplied together.", ex); } Prevention
- Treat factory/performer/stateChanger as all-or-nothing.
- Pass all three as null to use defaults.
- Migrate off the [Obsolete] advanced constructor.
When it happens
Trigger: Calling the advanced constructor with a non-null performer or stateChanger but a null factory, e.g. new BackgroundJobServer(opts, storage, extra, null, null, null, customPerformer, null) — factory is null while performer is set, so the else-branch throws on factory.
Common situations: Customizing job execution by supplying only the performer (or only the state changer) thinking the others default; partial migration to custom services; misunderstanding the all-or-nothing contract documented in the [Obsolete] advanced constructor.
Related errors
AI-assisted analysis of HangfireIO/Hangfire@c236dd0f93 (2026-08-13).
Data as JSON: /api/errors/8974f4cf80d7ee08.
Report an issue: GitHub.