dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'action')

Error message

Value cannot be null. (Parameter 'action')

What it means

DispatcherScheduler.Schedule<TState>(state, action) throws ArgumentNullException when the action delegate is null. The action is what gets BeginInvoke'd on the Dispatcher; there is nothing meaningful to schedule without it.

Solutions

  1. Ensure a non-null Func<IScheduler, TState, IDisposable> is passed to Schedule.
  2. If the work may be absent, schedule a no-op: (scheduler, state) => Disposable.Empty.
  3. Check any wrapper around IScheduler so it propagates real delegates rather than null.

Example fix

// before
scheduler.Schedule(state, null);
// after
scheduler.Schedule(state, (sched, st) => { DoWork(st); return Disposable.Empty; });
Defensive patterns

Strategy: validation

Validate before calling

if (action == null) throw new InvalidOperationException("schedule action required");

Type guard

bool HasAction<TState>(Func<IScheduler, TState, IDisposable> a) => a is not null;

Try / catch

try { scheduler.Schedule(state, action); } catch (ArgumentNullException ex) when (ex.ParamName == "action") { /* fix delegate */ }

Prevention

When it happens

Trigger: Calling scheduler.Schedule(state, null) directly, or passing a null action into Schedule(TimeSpan)/Schedule(DateTimeOffset) overloads that forward to this method (they null-check the forwarded delegate).

Common situations: Building a scheduling pipeline where the work delegate is produced dynamically and can be null; IScheduler abstraction code passing through a null action from an upstream operator; tests verifying argument checking (Schedule_ArgumentChecking).

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive.Wpf/System.Reactive.Concurrency/DispatcherScheduler.cs:87

        /// <summary>
        /// Gets the priority at which work items will be dispatched.
        /// </summary>
        public System.Windows.Threading.DispatcherPriority Priority { get; }

        /// <summary>
        /// Schedules an action to be executed on the dispatcher.
        /// </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)
            {
                throw new ArgumentNullException(nameof(action));
            }

            var d = new SingleAssignmentDisposable();

            Dispatcher.BeginInvoke(
                new Action(() =>
                {
                    if (!d.IsDisposed)
                    {
                        d.Disposable = action(this, state);
                    }
                }),
                Priority
            );

            return d;
        }

View on GitHub (pinned to 94b5d5ab91)