dotnet/reactive · error · ArgumentNullException

scheduler

Error message

scheduler

What it means

Expand(source, selector, scheduler) uses the IScheduler to control timing/concurrency of recursive expansion. A null scheduler throws ArgumentNullException immediately. Passing Scheduler.Default or an explicit scheduler resolves it.

Solutions

  1. Pass Scheduler.Default, Scheduler.TaskPool, or Scheduler.CurrentThread
  2. Drop the scheduler overload and call the two-argument Expand(source, selector), which uses the default scheduler
  3. Fix initialization/DI ordering so the injected scheduler is set before pipelines are built

Example fix

// before
var expanded = source.Expand(x => Next(x), sched); // sched null
// after
var expanded = source.Expand(x => Next(x), sched ?? Scheduler.Default);
Defensive patterns

Strategy: validation

Validate before calling

if (scheduler == null) throw new ArgumentNullException(nameof(scheduler), "Expand requires a scheduler; use Scheduler.Default.");

Type guard

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

Try / catch

try { var expanded = source.Expand(selector, scheduler); }
catch (ArgumentNullException ex) when (ex.ParamName == "scheduler") { log.Error("Scheduler not provided for Expand", ex); }

Prevention

When it happens

Trigger: Calling Expand with scheduler = null, typically from an uninitialized injected scheduler or a static property that has not been assigned.

Common situations: Missing IScheduler DI registration, unit tests passing null instead of TestScheduler, app-startup ordering where the scheduler field is read before initialization.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Linq/ObservableEx.cs:87

        /// <param name="scheduler">Scheduler on which to perform the expansion by enumerating the internal queue of obtained sequences.</param>
        /// <returns>An observable sequence containing all the elements produced by the recursive expansion.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="selector"/> or <paramref name="scheduler"/> is null.</exception>
        [Experimental]
        public static IObservable<TSource> Expand<TSource>(this IObservable<TSource> source, Func<TSource, IObservable<TSource>> selector, IScheduler scheduler)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

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

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

            return s_impl.Expand(source, selector, scheduler);
        }

        /// <summary>
        /// Expands an observable sequence by recursively invoking selector.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements in the source sequence and each of the recursively expanded sources obtained by running the selector function.</typeparam>
        /// <param name="source">Source sequence with the initial elements.</param>
        /// <param name="selector">Selector function to invoke for each produced element, resulting in another sequence to which the selector will be invoked recursively again.</param>
        /// <returns>An observable sequence containing all the elements produced by the recursive expansion.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="selector"/> is null.</exception>
        [Experimental]
        public static IObservable<TSource> Expand<TSource>(this IObservable<TSource> source, Func<TSource, IObservable<TSource>> selector)
        {
            if (source == null)
            {

View on GitHub (pinned to 94b5d5ab91)