HangfireIO/Hangfire · error · ArgumentNullException
threadFactory
Error message
threadFactory
What it means
Thrown by the public BackgroundTaskScheduler(Func<ThreadStart, IEnumerable<Thread>>, Action<Exception>) constructor when the threadFactory argument is null. The scheduler depends entirely on the factory to create its dedicated worker threads, so a null factory cannot proceed.
Source
Thrown at src/Hangfire.Core/Processing/BackgroundTaskScheduler.cs:109
: this(threadStart => DefaultThreadFactory(threadStart, threadCount), DefaultExceptionHandler)
{
}
/// <summary>Initializes a new instance of the <see cref="BackgroundTaskScheduler"/>
/// class with the specified <paramref name="threadFactory"/> and an optional exception
/// handler. All the created threads will be started to dispatch <see cref="Task"/>
/// instances scheduled to run on this scheduler.</summary>
/// <param name="threadFactory">Callback that creates one or more dedicated threads.</param>
/// <param name="exceptionHandler">Optional callback that is invoked when unhandled exception occurs
/// in one of the threads. After this event this instance is considered stopped.</param>
/// <exception cref="ArgumentNullException"><paramref name="threadFactory"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="threadFactory"/> returned <see langword="null"/> or zero threads.</exception>
/// <exception cref="ArgumentException"><paramref name="threadFactory"/> returned at least one thread not in the <see cref="ThreadState.Unstarted"/> state.</exception>
public BackgroundTaskScheduler(
[NotNull] Func<ThreadStart, IEnumerable<Thread>> threadFactory,
[CanBeNull] Action<Exception> exceptionHandler)
{
if (threadFactory == null) throw new ArgumentNullException(nameof(threadFactory));
_exceptionHandler = exceptionHandler;
_semaphore = new Semaphore(0, Int32.MaxValue);
// Stopped event should always be the first in this array, see the DispatchLoop method.
_waitHandles = new WaitHandle[] { _stopped, _semaphore };
#if !NETSTANDARD1_3
AppDomainUnloadMonitor.EnsureInitialized();
#endif
_threads = threadFactory(DispatchLoop)?.ToArray();
if (_threads == null || _threads.Length == 0)
{
throw new ArgumentException("At least one non-started thread should be created.", nameof(threadFactory));
}
View on GitHub (pinned to c236dd0f93)
Solutions
- Pass a non-null Func<ThreadStart, IEnumerable<Thread>>; use the parameterless ctor or the (int) ctor if you want the built-in factory.
- Guard the call site: throw a descriptive exception if the factory is null before constructing.
- Verify the source producing the factory is initialised.
Example fix
// before
var scheduler = new BackgroundTaskScheduler(null, ex => Log(ex));
// after
var scheduler = new BackgroundTaskScheduler(
start => Enumerable.Range(0, 4).Select(i => new Thread(start) { IsBackground = true, Name = $"Worker #{i}" }),
ex => Log(ex)); Defensive patterns
Strategy: validation
Validate before calling
if (threadFactory == null) throw new ArgumentNullException(nameof(threadFactory)); var scheduler = new BackgroundTaskScheduler(threadFactory, handler);
Try / catch
try { var s = new BackgroundTaskScheduler(factory, handler); }
catch (ArgumentNullException ex) when (ex.ParamName == nameof(threadFactory))
{ factory = DefaultFactory; /* retry */ } Prevention
- Use the parameterless or (int) constructor unless you need a custom factory.
- Resolve the factory before constructing the scheduler.
- Never pass a conditional expression that can evaluate to null.
When it happens
Trigger: Calling `new BackgroundTaskScheduler(null, handler)` or passing a null threadFactory. Also reachable if a caller resolves the factory lazily and it comes back null.
Common situations: Custom hosting code that builds a BackgroundTaskScheduler with a conditional factory that evaluated to null; a refactor that removed the factory expression; DI misregistration returning null for the factory delegate.
Related errors
- taskScheduler
- At least one non-started thread should be created.
- All the threads should be non-null and in the ThreadState.Un
- threadCount
- waitHandle
AI-assisted analysis of HangfireIO/Hangfire@c236dd0f93 (2026-08-13).
Data as JSON: /api/errors/02c222d35aea1161.
Report an issue: GitHub.