dotnet/reactive · error · ArgumentNullException

ArgumentNullException

Error message

ArgumentNullException

What it means

In the same clock-taking AsyncObserver.TimeInterval overload, a null IClock is rejected because the implementation reads clock.Now both to seed `last` and to compute each interval. Without a clock the operator cannot measure time, so ArgumentNullException is thrown before any subscription work. The exception is raised from the same public method, only from the clock check.

Solutions

  1. Pass a valid clock such as Clock.Default, Clock.Virtual/TestClock, or a mock
  2. Use AsyncObserver.TimeInterval(observer) which defaults to Clock.Default
  3. Fix the null-producing clock resolution

Example fix

// before
var obs = AsyncObserver.TimeInterval<int>(observer, clock); // clock null
// after
var obs = AsyncObserver.TimeInterval<int>(observer, clock ?? Clock.Default);
Defensive patterns

Strategy: validation

Validate before calling

if (clock is null) clock = Clock.Default;
var obs = AsyncObserver.TimeInterval<int>(observer, clock);

Type guard

bool IsValidClock(IClock? c) => c is not null;

Try / catch

try
{
    obs = AsyncObserver.TimeInterval<int>(observer, clock);
}
catch (ArgumentNullException ex) when (ex.ParamName == "clock")
{
    obs = AsyncObserver.TimeInterval<int>(observer); // uses Clock.Default
}

Prevention

When it happens

Trigger: Calling AsyncObserver.TimeInterval<TSource>(observer, clock) with clock == null; parameterless overload users are unaffected since it substitutes Clock.Default.

Common situations: DI/config-resolved IClock not registered; test code passing a mock clock variable left null; swapping time sources during migration to System.TimeProvider-like abstractions.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/TimeInterval.cs:48

        }
    }

    public partial class AsyncObserver
    {
        public static IAsyncObserver<TSource> TimeInterval<TSource>(IAsyncObserver<TimeInterval<TSource>> observer)
        {
            if (observer == null)
                throw new ArgumentNullException(nameof(observer));

            return TimeInterval(observer, Clock.Default);
        }

        public static IAsyncObserver<TSource> TimeInterval<TSource>(IAsyncObserver<TimeInterval<TSource>> observer, IClock clock)
        {
            if (observer == null)
                throw new ArgumentNullException(nameof(observer));
            if (clock == null)
                throw new ArgumentNullException(nameof(clock));

            var last = clock.Now;

            return Select<TSource, TimeInterval<TSource>>(observer, x =>
            {
                var now = clock.Now;
                var interval = now - last;
                last = now;

                return new TimeInterval<TSource>(x, interval);
            });
        }
    }
}

View on GitHub (pinned to 94b5d5ab91)