dotnet/reactive · error · ArgumentNullException

ArgumentNullException: keySelector

Error message

ArgumentNullException: keySelector

What it means

Thrown when keySelector is null in the capacity overload of GroupByUntil (core overload with int capacity). As in every other GroupByUntil overload, the key extraction delegate is mandatory and validated eagerly at the call site.

Solutions

  1. Supply a non-null keySelector delegate
  2. Add validation in your wrapper before calling the core overload
  3. Fix the upstream factory/lookup that returned null

Example fix

// before
var grouped = GroupByUntil(obs, sub, selectors.Key /* null */, ds, 16);
// after
var sel = selectors.Key ?? throw new ArgumentNullException(nameof(selectors.Key));
var grouped = GroupByUntil(obs, sub, sel, ds, 16);
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

bool IsValidSelector<TSource,TKey>(Func<TSource,TKey> f) => f is not null;

Try / catch

try
{
    var result = await GroupByUntil(observer, subscription, ks, ds, capacity);
}
catch (ArgumentNullException ex) when (ex.ParamName == "keySelector")
{
    // fix selector wiring upstream
}

Prevention

When it happens

Trigger: Calling GroupByUntil(observer, subscription, null, durationSelector, capacity) or forwarding a null selector from a wrapper.

Common situations: DI-resolved or config-built key selectors that came out null; wrappers that do not re-validate user-supplied delegates before delegating to the core overload.

Related errors


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

Appendix: source

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

                throw new ArgumentNullException(nameof(subscription));
            if (keySelector == null)
                throw new ArgumentNullException(nameof(keySelector));
            if (durationSelector == null)
                throw new ArgumentNullException(nameof(durationSelector));
            if (comparer == null)
                throw new ArgumentNullException(nameof(comparer));

            return GroupByUntil(observer, subscription, keySelector, durationSelector, int.MaxValue, comparer);
        }

        public static ValueTask<(IAsyncObserver<TSource>, IAsyncDisposable)> GroupByUntil<TSource, TKey, TDuration>(IAsyncObserver<IGroupedAsyncObservable<TKey, TSource>> observer, IAsyncDisposable subscription, Func<TSource, TKey> keySelector, Func<IGroupedAsyncObservable<TKey, TSource>, IAsyncObservable<TDuration>> durationSelector, int capacity)
        {
            if (observer == null)
                throw new ArgumentNullException(nameof(observer));
            if (subscription == null)
                throw new ArgumentNullException(nameof(subscription));
            if (keySelector == null)
                throw new ArgumentNullException(nameof(keySelector));
            if (durationSelector == null)
                throw new ArgumentNullException(nameof(durationSelector));
            if (capacity < 0)
                throw new ArgumentOutOfRangeException(nameof(capacity));

            return GroupByUntil(observer, subscription, keySelector, durationSelector, capacity, EqualityComparer<TKey>.Default);
        }

        public static ValueTask<(IAsyncObserver<TSource>, IAsyncDisposable)> GroupByUntil<TSource, TKey, TDuration>(IAsyncObserver<IGroupedAsyncObservable<TKey, TSource>> observer, IAsyncDisposable subscription, Func<TSource, TKey> keySelector, Func<IGroupedAsyncObservable<TKey, TSource>, IAsyncObservable<TDuration>> durationSelector, int capacity, IEqualityComparer<TKey> comparer)
        {
            if (observer == null)
                throw new ArgumentNullException(nameof(observer));
            if (subscription == null)
                throw new ArgumentNullException(nameof(subscription));
            if (keySelector == null)
                throw new ArgumentNullException(nameof(keySelector));
            if (durationSelector == null)
                throw new ArgumentNullException(nameof(durationSelector));

View on GitHub (pinned to 94b5d5ab91)