dotnet/reactive · error · ArgumentNullException

action (Parameter 'action')

Error message

action (Parameter 'action')

What it means

The same ScheduledItem<TAbsolute, TValue> constructor throws ArgumentNullException when the action argument is null. The action (Func<IScheduler, TValue, IDisposable>) is invoked when the item becomes due; without it the scheduler would invoke a null delegate when the queue is drained.

Solutions

  1. Ensure a non-null action delegate is supplied; use '(_, __) => Disposable.Empty' for no-op items.
  2. Validate the action in your wrapper before constructing the ScheduledItem.
  3. Trace the scheduling call chain (Enqueue/Invoke paths) to find where the null originates.
  4. Prefer building items via IScheduler.Schedule extension methods, which validate arguments.

Example fix

// before
var item = new ScheduledItem<DateTimeOffset, int>(scheduler, state, action /* null */, dueTime, comparer);
// after
if (action == null) throw new ArgumentNullException(nameof(action));
var item = new ScheduledItem<DateTimeOffset, int>(scheduler, state, action, dueTime, comparer);
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

bool HasAction<TValue>(Func<IScheduler, TValue, IDisposable> action) => action is not null;

Try / catch

try
{
    var item = new ScheduledItem<DateTimeOffset, TState>(scheduler, state, action, dueTime, comparer);
}
catch (ArgumentNullException ex) when (ex.ParamName == "action")
{
    var item = new ScheduledItem<DateTimeOffset, TState>(scheduler, state, (_, __) => Disposable.Empty, dueTime, comparer);
}

Prevention

When it happens

Trigger: Constructing ScheduledItem with a null Func<IScheduler, TValue, IDisposable>, often from generic scheduling helpers that pass through user-provided lambdas without validation.

Common situations: Custom scheduler implementations re-implementing Schedule internally; test fakes building materialized work items; refactoring where a lambda was replaced by a null-returning factory method.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Concurrency/ScheduledItem.cs:187

        private readonly IScheduler _scheduler;
        private readonly TValue _state;
        private readonly Func<IScheduler, TValue, IDisposable> _action;

        /// <summary>
        /// Creates a materialized work item.
        /// </summary>
        /// <param name="scheduler">Recursive scheduler to invoke the scheduled action with.</param>
        /// <param name="state">State to pass to the scheduled action.</param>
        /// <param name="action">Scheduled action.</param>
        /// <param name="dueTime">Time at which to run the scheduled action.</param>
        /// <param name="comparer">Comparer used to compare work items based on their scheduled time.</param>
        /// <exception cref="ArgumentNullException"><paramref name="scheduler"/> or <paramref name="action"/> or <paramref name="comparer"/> is <c>null</c>.</exception>
        public ScheduledItem(IScheduler scheduler, TValue state, Func<IScheduler, TValue, IDisposable> action, TAbsolute dueTime, IComparer<TAbsolute> comparer)
            : base(dueTime, comparer)
        {
            _scheduler = scheduler ?? throw new ArgumentNullException(nameof(scheduler));
            _state = state;
            _action = action ?? throw new ArgumentNullException(nameof(action));
        }

        /// <summary>
        /// Creates a materialized work item.
        /// </summary>
        /// <param name="scheduler">Recursive scheduler to invoke the scheduled action with.</param>
        /// <param name="state">State to pass to the scheduled action.</param>
        /// <param name="action">Scheduled action.</param>
        /// <param name="dueTime">Time at which to run the scheduled action.</param>
        /// <exception cref="ArgumentNullException"><paramref name="scheduler"/> or <paramref name="action"/> is <c>null</c>.</exception>
        public ScheduledItem(IScheduler scheduler, TValue state, Func<IScheduler, TValue, IDisposable> action, TAbsolute dueTime)
            : this(scheduler, state, action, dueTime, Comparer<TAbsolute>.Default)
        {
        }

        /// <summary>
        /// Invokes the scheduled action with the supplied recursive scheduler and state.
        /// </summary>

View on GitHub (pinned to 94b5d5ab91)