dotnet/reactive · error · ArgumentNullException

ArgumentNullException: elementSelector

Error message

ArgumentNullException: elementSelector

What it means

GroupByUntil throws ArgumentNullException when elementSelector is null. The elementSelector projects each source element into the element emitted by the grouped observable; without it the operator cannot construct groups. The library validates arguments eagerly so the failure points at the caller rather than inside the async pipeline.

Solutions

  1. Pass an identity projection if no transformation is needed: o => o.
  2. Supply the actual projection lambda, e.g. o => o.Lines.
  3. Default an optional selector: elementSelector ?? (x => x).
  4. Verify parameter order — keySelector, elementSelector, durationSelector — in the overload you call.

Example fix

// before
var groups = source.GroupByUntil(o => o.Type, null, g => g.TakeUntil(done));
// after
var groups = source.GroupByUntil(o => o.Type, o => o, g => g.TakeUntil(done));
Defensive patterns

Strategy: validation

Validate before calling

if (elementSelector == null) throw new InvalidOperationException("GroupByUntil requires a non-null elementSelector; use o => o for identity");
var groups = source.GroupByUntil(keySelector, elementSelector, durationSelector);

Type guard

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

Try / catch

try
{
    var groups = source.GroupByUntil(keySelector, elementSelector, durationSelector);
}
catch (ArgumentNullException ex) when (ex.ParamName == "elementSelector")
{
    // substitute identity projection and retry
}

Prevention

When it happens

Trigger: Calling a public GroupByUntil overload at GroupByUntil.cs:392 (the 3-delegate overload forwarding to the full overload with int.MaxValue capacity and default comparer) with null as the elementSelector argument.

Common situations: Passing null instead of an identity lambda (o => o) when no element projection is needed; a projection delegate variable left null after conditional setup; misuse of an overload whose parameter order was misread.

Related errors


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

Appendix: source

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

                throw new ArgumentNullException(nameof(durationSelector));
            if (capacity < 0)
                throw new ArgumentOutOfRangeException(nameof(capacity));
            if (comparer == null)
                throw new ArgumentNullException(nameof(comparer));

            return GroupByUntil(observer, subscription, x => new ValueTask<TKey>(keySelector(x)), durationSelector, capacity, comparer);
        }

        public static ValueTask<(IAsyncObserver<TSource>, IAsyncDisposable)> GroupByUntil<TSource, TKey, TElement, TDuration>(IAsyncObserver<IGroupedAsyncObservable<TKey, TElement>> observer, IAsyncDisposable subscription, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<IGroupedAsyncObservable<TKey, TElement>, IAsyncObservable<TDuration>> durationSelector)
        {
            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 (elementSelector == null)
                throw new ArgumentNullException(nameof(elementSelector));
            if (durationSelector == null)
                throw new ArgumentNullException(nameof(durationSelector));

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

        public static ValueTask<(IAsyncObserver<TSource>, IAsyncDisposable)> GroupByUntil<TSource, TKey, TElement, TDuration>(IAsyncObserver<IGroupedAsyncObservable<TKey, TElement>> observer, IAsyncDisposable subscription, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<IGroupedAsyncObservable<TKey, TElement>, IAsyncObservable<TDuration>> durationSelector, 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 (elementSelector == null)
                throw new ArgumentNullException(nameof(elementSelector));
            if (durationSelector == null)
                throw new ArgumentNullException(nameof(durationSelector));

View on GitHub (pinned to 94b5d5ab91)