dotnet/reactive · error · ArgumentNullException

clock

Error message

clock

What it means

The SkipLast(duration, clock) overload throws ArgumentNullException("clock") when the IClock parameter is null. The clock is required to timestamp elements and decide which trailing elements fall within the skip window. The library fails fast at the public entry point.

Solutions

  1. Pass a valid IClock instance (e.g. the same LocalClock/ConcreteClock used elsewhere, or a test clock like VirtualClock for tests).
  2. Register and inject IClock properly in your DI container.
  3. Default the clock at the call site: clock ?? Clock.Default (or the library's default clock) if the overload without clock is not suitable.

Example fix

// before
var res = source.SkipLast(TimeSpan.FromSeconds(1), testClock); // testClock is null
// after
var res = source.SkipLast(TimeSpan.FromSeconds(1), testClock ?? new LocalClock());
Defensive patterns

Strategy: validation

Validate before calling

if (clock == null) throw new InvalidOperationException("SkipLast(duration, clock) requires a non-null IClock");
var res = source.SkipLast(duration, clock);

Type guard

static bool HasClock(IClock c) => c != null;

Try / catch

try { var res = source.SkipLast(duration, clock); }
catch (ArgumentNullException ex) when (ex.ParamName == "clock") { /* bind a default clock and retry */ }

Prevention

When it happens

Trigger: Calling source.SkipLast(TimeSpan.FromSeconds(1), clock) where clock is null — e.g. a test clock, virtual-time clock, or injected IClock that was never assigned.

Common situations: Forgetting to provide a test/virtual clock in unit tests; DI not binding IClock; a clock property initialized lazily and still null at composition time.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/SkipLast.cs:57

            if (duration == TimeSpan.Zero)
            {
                return source;
            }

            return CreateAsyncObservable<TSource>.From(
                source,
                duration,
                static (source, duration, observer) => source.SubscribeSafeAsync(AsyncObserver.SkipLast(observer, duration)));
        }

        public static IAsyncObservable<TSource> SkipLast<TSource>(this IAsyncObservable<TSource> source, TimeSpan duration, IClock clock)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (duration < TimeSpan.Zero)
                throw new ArgumentOutOfRangeException(nameof(duration));
            if (clock == null)
                throw new ArgumentNullException(nameof(clock));

            if (duration == TimeSpan.Zero)
            {
                return source;
            }

            return CreateAsyncObservable<TSource>.From(
                source,
                (duration, clock),
                static (source, state, observer) => source.SubscribeSafeAsync(AsyncObserver.SkipLast(observer, state.duration, state.clock)));
        }
    }

    public partial class AsyncObserver
    {
        public static IAsyncObserver<TSource> SkipLast<TSource>(IAsyncObserver<TSource> observer, int count)
        {
            if (observer == null)

View on GitHub (pinned to 94b5d5ab91)