dotnet/reactive · error · ArgumentNullException

new ArgumentNullException(nameof(timeSelector))

Error message

new ArgumentNullException(nameof(timeSelector))

What it means

The fully-async Generate overload throws ArgumentNullException when the timeSelector delegate is null. timeSelector computes the per-element delay (TimeSpan in this overload); a null value would leave the scheduler with no delay to apply between OnNext emissions, so it is rejected upfront.

Solutions

  1. Supply a timeSelector, e.g. s => new ValueTask<TimeSpan>(TimeSpan.FromSeconds(1)).
  2. If no delay is desired, pass s => new ValueTask<TimeSpan>(TimeSpan.Zero) instead of null.
  3. Double-check which Generate overload you are calling and switch to the non-timed overload if you do not need per-element delays.

Example fix

// before
Generate(observer, 0, cond, iter, sel, null, sched); // throws
// after
Generate(observer, 0, cond, iter, sel, s => new ValueTask<TimeSpan>(TimeSpan.FromSeconds(1)), sched);
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

static bool HasTimeSelector<TState>(Func<TState, ValueTask<TimeSpan>> ts) => ts is not null;

Try / catch

try { await Generate(observer, state, cond, iter, sel, timeSel, sched); } catch (ArgumentNullException ex) when (ex.ParamName == "timeSelector") { /* fall back to TimeSpan.Zero selector */ }

Prevention

When it happens

Trigger: Calling Generate(observer, initialState, condition, iterate, resultSelector, timeSelector, scheduler) with ValueTask delegates (Generate.cs:345) and passing null for Func<TState, ValueTask<TimeSpan>> timeSelector.

Common situations: Confusing overloads: the TimeSpan-based overload requires timeSelector while non-timed variants do not, so copying a call from a non-timed overload omits it; a delay-strategy object whose ToSelector() returned null.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/Generate.cs:356

                throw new ArgumentNullException(nameof(resultSelector));
            if (timeSelector == null)
                throw new ArgumentNullException(nameof(timeSelector));

            return Generate(observer, initialState, condition, iterate, resultSelector, timeSelector, TaskPoolAsyncScheduler.Default);
        }

        public static ValueTask<IAsyncDisposable> Generate<TState, TResult>(IAsyncObserver<TResult> observer, TState initialState, Func<TState, ValueTask<bool>> condition, Func<TState, ValueTask<TState>> iterate, Func<TState, ValueTask<TResult>> resultSelector, Func<TState, ValueTask<TimeSpan>> timeSelector, IAsyncScheduler scheduler)
        {
            if (observer == null)
                throw new ArgumentNullException(nameof(observer));
            if (condition == null)
                throw new ArgumentNullException(nameof(condition));
            if (iterate == null)
                throw new ArgumentNullException(nameof(iterate));
            if (resultSelector == null)
                throw new ArgumentNullException(nameof(resultSelector));
            if (timeSelector == null)
                throw new ArgumentNullException(nameof(timeSelector));
            if (scheduler == null)
                throw new ArgumentNullException(nameof(scheduler));

            return scheduler.ScheduleAsync(async ct =>
            {
                var first = true;
                var state = initialState;

                while (!ct.IsCancellationRequested)
                {
                    var hasResult = false;
                    var result = default(TResult);
                    var nextDue = default(TimeSpan);

                    try
                    {
                        if (first)
                        {

View on GitHub (pinned to 94b5d5ab91)