dotnet/reactive · error · ArgumentNullException

new ArgumentNullException(nameof(keySelector))

Error message

new ArgumentNullException(nameof(keySelector))

What it means

GroupBy requires a keySelector (Func<TSource, TKey>) to compute each element's group key. A null selector would make grouping impossible, so the library throws ArgumentNullException(nameof(keySelector)) right after validating the source.

Solutions

  1. Pass a concrete key selector, e.g. x => x.CategoryId.
  2. If all elements belong to one group, use a constant selector x => 0 rather than null.
  3. Null-check the configured selector in your wrapper before delegating to GroupBy.

Example fix

// before
var groups = source.GroupBy(null);
// after
var groups = source.GroupBy(x => x.Key);
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

static bool IsValidKeySelector<TSource, TKey>(Func<TSource, TKey>? k) => k is not null;

Try / catch

try { var groups = source.GroupBy(keySelector); } catch (ArgumentNullException ex) when (ex.ParamName == "keySelector") { keySelector = static _ => default!; }

Prevention

When it happens

Trigger: Calling source.GroupBy(null) or passing a null delegate through from a helper/lambda factory — e.g. a key-extraction function resolved from configuration or a method group that failed to bind.

Common situations: Key mapping policy not configured; generic grouping helper that forwards an optional selector defaulting to null; refactoring that left the selector argument empty in a positional call.

Related errors


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

Appendix: source

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

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT License.
// See the LICENSE file in the project root for more information. 

using System.Collections.Generic;
using System.Reactive.Disposables;
using System.Reactive.Subjects;
using System.Threading.Tasks;

namespace System.Reactive.Linq
{
    public partial class AsyncObservable
    {
        public static IAsyncObservable<IGroupedAsyncObservable<TKey, TSource>> GroupBy<TSource, TKey>(this IAsyncObservable<TSource> source, Func<TSource, TKey> keySelector)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (keySelector == null)
                throw new ArgumentNullException(nameof(keySelector));

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

        public static IAsyncObservable<IGroupedAsyncObservable<TKey, TSource>> GroupBy<TSource, TKey>(this IAsyncObservable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (keySelector == null)
                throw new ArgumentNullException(nameof(keySelector));
            if (comparer == null)
                throw new ArgumentNullException(nameof(comparer));

            return CreateAsyncObservable<IGroupedAsyncObservable<TKey, TSource>>.From(
                source,

View on GitHub (pinned to 94b5d5ab91)