dotnet/reactive · error · ArgumentNullException

scheduler

Error message

scheduler

What it means

The scheduler-taking DelaySubscription overload throws ArgumentNullException because the scheduler argument is null. The scheduler determines when the delayed Subscribe happens; without one the operator cannot function, so the null is rejected up front rather than at subscription time.

Solutions

  1. Pass TaskPoolAsyncScheduler.Default or another non-null IAsyncScheduler.
  2. Use the DelaySubscription(source, dueTime) overload that defaults to TaskPoolAsyncScheduler.Default.
  3. Ensure DI/config-provided schedulers are registered and resolved before composition.

Example fix

// before
return source.DelaySubscription(due, cfg.Scheduler); // cfg.Scheduler null
// after
return source.DelaySubscription(due, cfg.Scheduler ?? TaskPoolAsyncScheduler.Default);
Defensive patterns

Strategy: validation

Validate before calling

var scheduler = cfg.Scheduler ?? TaskPoolAsyncScheduler.Default;
return source.DelaySubscription(due, scheduler);

Type guard

static IAsyncScheduler SchedulerOrDefault(IAsyncScheduler? s) => s ?? TaskPoolAsyncScheduler.Default;

Try / catch

try { return source.DelaySubscription(due, scheduler); }
catch (ArgumentNullException ex) when (ex.ParamName == "scheduler") { return source.DelaySubscription(due, TaskPoolAsyncScheduler.Default); }

Prevention

When it happens

Trigger: Calling DelaySubscription<TSource>(source, dueTime, null) — null IAsyncScheduler on the second overload.

Common situations: Scheduler resolved from configuration that was absent; passing a test scheduler variable that is only initialized in test mode; forgetting the default-arg overload exists.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/DelaySubscription.cs:25

namespace System.Reactive.Linq
{
    public partial class AsyncObservable
    {
        public static IAsyncObservable<TSource> DelaySubscription<TSource>(this IAsyncObservable<TSource> source, TimeSpan dueTime)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));

            return DelaySubscription(source, dueTime, TaskPoolAsyncScheduler.Default);
        }

        public static IAsyncObservable<TSource> DelaySubscription<TSource>(this IAsyncObservable<TSource> source, TimeSpan dueTime, IAsyncScheduler scheduler)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (scheduler == null)
                throw new ArgumentNullException(nameof(scheduler));

            return Create(
                source,
                (dueTime, scheduler),
                static async (source, state, observer) =>
                {
                    var d = new CompositeAsyncDisposable();

                    var task = await state.scheduler.ScheduleAsync(async ct =>
                    {
                        var inner = await source.SubscribeSafeAsync(observer).ConfigureAwait(false);
                        await d.AddAsync(inner).ConfigureAwait(false);
                    }, state.dueTime).ConfigureAwait(false);

                    await d.AddAsync(task).ConfigureAwait(false);

                    return d;
                });

View on GitHub (pinned to 94b5d5ab91)