dotnet/reactive · error · ArgumentNullException

scheduler

Error message

scheduler

What it means

The scheduled FromEvent<TDelegate,TEventArgs> overload throws ArgumentNullException when scheduler is null. The scheduler is used to marshal event notifications and is validated up-front per Rx argument contracts.

Solutions

  1. Pass Scheduler.Default or another concrete IScheduler instance.
  2. In tests, supply a TestScheduler/ImmediateScheduler explicitly.
  3. Fix DI registrations so IScheduler is always provided.
  4. Drop to the non-scheduler FromEvent overload if scheduling is unnecessary.

Example fix

// before
Observable.FromEvent<EventHandler, EventArgs>(conv, add, remove, null);
// after
Observable.FromEvent<EventHandler, EventArgs>(conv, add, remove, Scheduler.Default);
Defensive patterns

Strategy: validation

Validate before calling

if (scheduler is null) scheduler = Scheduler.Default;

Type guard

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

Try / catch

try { return Observable.FromEvent(conv, add, remove, scheduler); } catch (ArgumentNullException ex) when (ex.ParamName == "scheduler") { return Observable.FromEvent(conv, add, remove, Scheduler.Default); }

Prevention

When it happens

Trigger: Calling FromEvent(conversion, addHandler, removeHandler, null); a null scheduler from an unresolved dependency or default parameter left unset.

Common situations: Unit tests forgetting to supply a TestScheduler; DI container without IScheduler registration; scheduler field made nullable during refactoring.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Linq/Observable.Events.cs:1235

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

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

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

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

            return s_impl.FromEvent(conversion, addHandler, removeHandler, scheduler);
        }

        /// <summary>
        /// Converts a .NET event to an observable sequence, using a supplied event delegate type. Each event invocation is surfaced through an OnNext message in the resulting sequence.
        /// For conversion of events conforming to the standard .NET event pattern, use any of the FromEventPattern overloads instead.
        /// </summary>
        /// <typeparam name="TDelegate">The delegate type of the event to be converted.</typeparam>
        /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
        /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
        /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
        /// <returns>The observable sequence that contains the event argument objects passed to the invocations of the underlying .NET event.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="addHandler"/> or <paramref name="removeHandler"/> is null.</exception>
        /// <remarks>
        /// <para>
        /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.

View on GitHub (pinned to 94b5d5ab91)