dotnet/reactive · error · ArgumentNullException

threadFactory (Parameter 'threadFactory')

Error message

threadFactory (Parameter 'threadFactory')

What it means

The NewThreadScheduler(Func<ThreadStart, Thread>) constructor throws ArgumentNullException when threadFactory is null. The factory is the core mechanism this scheduler uses to create a thread per unit of work, so without it the scheduler cannot function at all.

Solutions

  1. Pass a valid factory such as 'static ThreadStart ts => new Thread(ts)' to the constructor.
  2. Use NewThreadScheduler.Default (or Scheduler.NewThread) instead of constructing manually.
  3. Guard the factory argument with ArgumentNullException.ThrowIfNull before constructing.
  4. Fix configuration resolution so the factory delegate is not null.

Example fix

// before
var scheduler = new NewThreadScheduler(config.ThreadFactory); // null when unset
// after
var scheduler = config.ThreadFactory is null
    ? NewThreadScheduler.Default
    : new NewThreadScheduler(config.ThreadFactory);
Defensive patterns

Strategy: validation

Validate before calling

if (threadFactory == null)
    threadFactory = ts => new Thread(ts); // or use NewThreadScheduler.Default

Type guard

bool HasThreadFactory(Func<ThreadStart, Thread> f) => f is not null;

Try / catch

try
{
    var s = new NewThreadScheduler(threadFactory);
}
catch (ArgumentNullException ex) when (ex.ParamName == "threadFactory")
{
    var s = NewThreadScheduler.Default;
}

Prevention

When it happens

Trigger: Calling new NewThreadScheduler(null), or NewThreadScheduler.Default-style paths where a custom factory resolved from configuration/lambda is null.

Common situations: Building a scheduler from settings where the factory delegate is read as null; refactoring away Scheduler.NewThread internals; passing a method group that fails to bind (e.g. wrong overload) yielding null after a ternary.

Related errors


AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15). Data as JSON: /api/errors/d0815b8a210c91a9. Report an issue: GitHub.

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Concurrency/NewThreadScheduler.cs:39

        /// </summary>
        public NewThreadScheduler()
            : this(action => new Thread(action))
        {
        }

        /// <summary>
        /// Gets an instance of this scheduler that uses the default Thread constructor.
        /// </summary>
        public static NewThreadScheduler Default => Instance.Value;

        /// <summary>
        /// Creates an object that schedules each unit of work on a separate thread.
        /// </summary>
        /// <param name="threadFactory">Factory function for thread creation.</param>
        /// <exception cref="ArgumentNullException"><paramref name="threadFactory"/> is <c>null</c>.</exception>
        public NewThreadScheduler(Func<ThreadStart, Thread> threadFactory)
        {
            _threadFactory = threadFactory ?? throw new ArgumentNullException(nameof(threadFactory));
        }

        /// <summary>
        /// Schedules an action to be executed after dueTime.
        /// </summary>
        /// <typeparam name="TState">The type of the state passed to the scheduled action.</typeparam>
        /// <param name="state">State passed to the action to be executed.</param>
        /// <param name="action">Action to be executed.</param>
        /// <param name="dueTime">Relative time after which to execute the action.</param>
        /// <returns>The disposable object used to cancel the scheduled action (best effort).</returns>
        /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c>.</exception>
        public override IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
        {
            if (action == null)
            {
                throw new ArgumentNullException(nameof(action));
            }

View on GitHub (pinned to 94b5d5ab91)