dotnet/reactive · error · ArgumentNullException

scheduler (Value cannot be null)

Error message

scheduler (Value cannot be null)

What it means

SchedulePeriodic<TState>(IScheduler, TState, TimeSpan, Func<TState,TState>) in Scheduler.Services.Emulation.cs throws ArgumentNullException when the IScheduler is null. The extension method needs a scheduler instance to run the periodic loop on; a null receiver cannot be dispatched. Rx validates this eagerly rather than failing when the periodic timer starts.

Solutions

  1. Pass a concrete scheduler such as Scheduler.Default, Scheduler.ThreadPool, or a test scheduler (TestScheduler) instead of null
  2. If the scheduler is resolved from DI/config, ensure the binding exists and check for null before calling
  3. Guard the call site: if (scheduler is null) throw new ArgumentNullException(nameof(scheduler)); with a clearer message

Example fix

// before
IScheduler scheduler = config.GetScheduler(); // returns null
scheduler.SchedulePeriodic(state, TimeSpan.FromSeconds(1), s => s);

// after
IScheduler scheduler = config.GetScheduler() ?? Scheduler.Default;
scheduler.SchedulePeriodic(state, TimeSpan.FromSeconds(1), s => s);
Defensive patterns

Strategy: validation

Validate before calling

if (scheduler is null) throw new ArgumentNullException(nameof(scheduler));
if (period < TimeSpan.Zero) throw new ArgumentOutOfRangeException(nameof(period));
if (action is null) throw new ArgumentNullException(nameof(action));
scheduler.SchedulePeriodic(state, period, action);

Type guard

bool CanSchedulePeriodic(IScheduler s) => s is not null;

Try / catch

try { scheduler.SchedulePeriodic(state, period, action); }
catch (ArgumentNullException ex) when (ex.ParamName == "scheduler") { scheduler = Scheduler.Default; /* retry */ }

Prevention

When it happens

Trigger: Invoking scheduler.SchedulePeriodic(state, period, action) with a null scheduler reference, e.g. IScheduler s = null; s.SchedulePeriodic(state, TimeSpan.FromSeconds(1), x => x), or passing a scheduler factory/property that returned null.

Common situations: Injecting IScheduler via DI where no binding is registered (null default), a configuration-selected scheduler that fell back to null, calling through a wrapper class whose scheduler field was never initialized, or code moved off DefaultScheduler during a refactor.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Concurrency/Scheduler.Services.Emulation.cs:32

        /// <summary>
        /// Schedules a periodic piece of work by dynamically discovering the scheduler's capabilities.
        /// If the scheduler supports periodic scheduling, the request will be forwarded to the periodic scheduling implementation.
        /// If the scheduler provides stopwatch functionality, the periodic task will be emulated using recursive scheduling with a stopwatch to correct for time slippage.
        /// Otherwise, the periodic task will be emulated using recursive scheduling.
        /// </summary>
        /// <typeparam name="TState">The type of the state passed to the scheduled action.</typeparam>
        /// <param name="scheduler">The scheduler to run periodic work on.</param>
        /// <param name="state">Initial state passed to the action upon the first iteration.</param>
        /// <param name="period">Period for running the work periodically.</param>
        /// <param name="action">Action to be executed, potentially updating the state.</param>
        /// <returns>The disposable object used to cancel the scheduled recurring action (best effort).</returns>
        /// <exception cref="ArgumentNullException"><paramref name="scheduler"/> or <paramref name="action"/> is <c>null</c>.</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="period"/> is less than <see cref="TimeSpan.Zero"/>.</exception>
        public static IDisposable SchedulePeriodic<TState>(this IScheduler scheduler, TState state, TimeSpan period, Func<TState, TState> action)
        {
            if (scheduler == null)
            {
                throw new ArgumentNullException(nameof(scheduler));
            }

            if (period < TimeSpan.Zero)
            {
                throw new ArgumentOutOfRangeException(nameof(period));
            }

            if (action == null)
            {
                throw new ArgumentNullException(nameof(action));
            }

            return SchedulePeriodic_(scheduler, state, period, action);
        }

        /// <summary>
        /// Schedules a periodic piece of work by dynamically discovering the scheduler's capabilities.
        /// If the scheduler supports periodic scheduling, the request will be forwarded to the periodic scheduling implementation.

View on GitHub (pinned to 94b5d5ab91)