dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'action')

Error message

Value cannot be null. (Parameter 'action')

What it means

ControlScheduler.Schedule throws ArgumentNullException when the action delegate scheduled for execution is null. The guard exists because the scheduler would otherwise fail later, inside the WinForms message loop, when trying to invoke the delegate. Fail-fast at the public API boundary makes the caller's bug obvious.

Solutions

  1. Pass a non-null Func<IScheduler, TState, IDisposable> to Schedule.
  2. Check where the delegate comes from; initialize the field/property that holds it before scheduling.
  3. If the action may legitimately be absent, skip scheduling or use a no-op delegate such as static (s, st) => Disposable.Empty.

Example fix

// before
scheduler.Schedule(state, (Action<IScheduler,int>)null);
// after
scheduler.Schedule(state, (sch, st) => { DoWork(st); return Disposable.Empty; });
Defensive patterns

Strategy: validation

Validate before calling

if (action is null) throw new ArgumentNullException(nameof(action)); // or assert before scheduling
scheduler.Schedule(state, action);

Type guard

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

Try / catch

try { scheduler.Schedule(state, action); } catch (ArgumentNullException ex) when (ex.ParamName == "action") { Log("Scheduled action was null"); }

Prevention

When it happens

Trigger: Calling controlScheduler.Schedule(state, null) or any overload where the Func<IScheduler, TState, IDisposable> action argument is null. Also hit indirectly by internal wrappers (Schedule, SchedulePeriodic, ScheduleRelative_) that forward a null action.

Common situations: Passing the result of a method that returned null as the action, storing scheduler actions in a nullable field that was never initialized, or wiring up scheduled work via reflection/config where the delegate resolved to null.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive.Windows.Forms/ControlScheduler.cs:54

        /// <summary>
        /// Gets the control associated with the ControlScheduler.
        /// </summary>
        public Control Control => _control;

        /// <summary>
        /// Schedules an action to be executed on the message loop associated with the control.
        /// </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 null.</exception>
        public override IDisposable Schedule<TState>(TState state, Func<IScheduler, TState, IDisposable> action)
        {
            if (action == null)
            {
                throw new ArgumentNullException(nameof(action));
            }

            if (_control.IsDisposed)
            {
                return Disposable.Empty;
            }

            var d = new SingleAssignmentDisposable();

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

            return d;

View on GitHub (pinned to 94b5d5ab91)