dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'taskFactory')

Error message

Value cannot be null. (Parameter 'taskFactory')

What it means

The TaskPoolScheduler constructor throws ArgumentNullException when the TaskFactory is null. The scheduler relies on the factory to start tasks for each scheduled work item; a null factory would break every Schedule call, so Rx validates eagerly. TaskPoolScheduler.Default is the ready-made instance using TaskScheduler.Default.

Solutions

  1. Pass a valid TaskFactory, e.g. new TaskFactory() or new TaskFactory(cancellationToken, TaskCreationOptions, TaskContinuationOptions, taskScheduler).
  2. Use TaskPoolScheduler.Default if no custom factory is required.
  3. Fix the DI/config wiring that produced a null TaskFactory.

Example fix

// before
var sched = new TaskPoolScheduler(_taskFactory); // null
// after
var sched = _taskFactory != null
    ? new TaskPoolScheduler(_taskFactory)
    : TaskPoolScheduler.Default;
Defensive patterns

Strategy: validation

Validate before calling

if (taskFactory == null) taskFactory = TaskPoolScheduler.Default; // or new TaskFactory()

Type guard

bool hasFactory = taskFactory is not null;

Try / catch

try { var sched = new TaskPoolScheduler(taskFactory); }
catch (ArgumentNullException) { var sched = TaskPoolScheduler.Default; }

Prevention

When it happens

Trigger: new TaskPoolScheduler((TaskFactory)null), usually a TaskFactory field/property that was never initialized or a DI registration that yielded null.

Common situations: Configuring a custom TaskFactory (e.g. with a specific TaskScheduler or CancellationToken) where the configuration step silently failed; refactoring that removed factory initialization.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Concurrency/TaskPoolScheduler.cs:191

            public void Dispose()
            {
                _cancel.Dispose();
            }

            public bool IsDisposed => _cancel.IsDisposed;
        }

        private static readonly Lazy<TaskPoolScheduler> LazyInstance = new(static () => new TaskPoolScheduler(new TaskFactory(TaskScheduler.Default)));
        private readonly TaskFactory _taskFactory;

        /// <summary>
        /// Creates an object that schedules units of work using the provided <see cref="TaskFactory"/>.
        /// </summary>
        /// <param name="taskFactory">Task factory used to create tasks to run units of work.</param>
        /// <exception cref="ArgumentNullException"><paramref name="taskFactory"/> is <c>null</c>.</exception>
        public TaskPoolScheduler(TaskFactory taskFactory)
        {
            _taskFactory = taskFactory ?? throw new ArgumentNullException(nameof(taskFactory));
        }

        /// <summary>
        /// Gets an instance of this scheduler that uses the default <see cref="TaskScheduler"/>.
        /// </summary>
        public static TaskPoolScheduler Default => LazyInstance.Value;

        /// <summary>
        /// Schedules an action to be executed.
        /// </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>
        /// <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, Func<IScheduler, TState, IDisposable> action)
        {
            if (action == null)

View on GitHub (pinned to 94b5d5ab91)