dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'elementSelector')

Error message

Value cannot be null. (Parameter 'elementSelector')

What it means

AsyncRx.NET's GroupBy operator validates its arguments eagerly and throws ArgumentNullException when elementSelector is null. The elementSelector lambda maps each source element to the element exposed on each IGroupedAsyncObservable, so the library refuses to build the grouping pipeline without it. This is a fail-fast guard, not a runtime aggregation failure.

Solutions

  1. Pass a non-null element selector lambda, e.g. x => x, when the element is the source item itself.
  2. Check which GroupBy overload you intend; if no projection is needed use the overload without elementSelector instead of passing null.
  3. Add a null check or require!-style guard on the selector before calling GroupBy.

Example fix

// before
var grouped = source.GroupBy(x => x.Key, (Func<Item, Item>)null);
// after
var grouped = source.GroupBy(x => x.Key, x => x);
Defensive patterns

Strategy: validation

Validate before calling

if (source == null) throw new ArgumentNullException(nameof(source));
if (keySelector == null) throw new ArgumentNullException(nameof(keySelector));
if (elementSelector == null) throw new ArgumentNullException(nameof(elementSelector));
var grouped = source.GroupBy(keySelector, elementSelector);

Type guard

bool IsValid<T, TKey, TElem>(Func<T, TKey> k, Func<T, TElem> e) => k != null && e != null;

Try / catch

try
{
    var grouped = source.GroupBy(keySelector, elementSelector);
}
catch (ArgumentNullException ex) when (ex.ParamName == "elementSelector")
{
    grouped = source.GroupBy(keySelector, x => x); // identity projection fallback
}

Prevention

When it happens

Trigger: Calling any GroupBy overload that accepts an elementSelector (e.g. AsyncObservable.GroupBy(source, keySelector, elementSelector), with capacity, or with comparer) and passing null for elementSelector.

Common situations: Developers call a GroupBy overload they believe takes only a keySelector but accidentally supply an extra null argument; conditional lambda construction returns null; refactoring moves the element projection out and passes a nullable delegate field that was never assigned.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/GroupBy.cs:258

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

            return CreateAsyncObservable<IGroupedAsyncObservable<TKey, TElement>>.From(
                source,
                (keySelector, elementSelector, capacity),
                static (source, state, observer) => GroupByCore(source, observer, (o, d) => AsyncObserver.GroupBy(o, d, state.keySelector, state.elementSelector, state.capacity)));
        }

        public static IAsyncObservable<IGroupedAsyncObservable<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IAsyncObservable<TSource> source, Func<TSource, ValueTask<TKey>> keySelector, Func<TSource, ValueTask<TElement>> elementSelector, int capacity, IEqualityComparer<TKey> comparer)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (keySelector == null)
                throw new ArgumentNullException(nameof(keySelector));
            if (elementSelector == null)
                throw new ArgumentNullException(nameof(elementSelector));
            if (capacity < 0)
                throw new ArgumentOutOfRangeException(nameof(capacity));
            if (comparer == null)
                throw new ArgumentNullException(nameof(comparer));

            return CreateAsyncObservable< IGroupedAsyncObservable<TKey, TElement>>.From(
                source,
                (keySelector, elementSelector, capacity, comparer),
                static (source, state, observer) => GroupByCore(source, observer, (o, d) => AsyncObserver.GroupBy(o, d, state.keySelector, state.elementSelector, state.capacity, state.comparer)));
        }

        private static async ValueTask<IAsyncDisposable> GroupByCore<TSource, TKey, TElement>(IAsyncObservable<TSource> source, IAsyncObserver<IGroupedAsyncObservable<TKey, TElement>> observer, Func<IAsyncObserver<IGroupedAsyncObservable<TKey, TElement>>, IAsyncDisposable, (IAsyncObserver<TSource>, IAsyncDisposable)> createObserver)
        {
            var d = new SingleAssignmentAsyncDisposable();

            var (sink, subscription) = createObserver(observer, d);

            var inner = await source.SubscribeSafeAsync(sink).ConfigureAwait(false);

View on GitHub (pinned to 94b5d5ab91)