dotnet/reactive · error · ArgumentNullException
Value cannot be null. (Parameter 'comparer')
Error message
Value cannot be null. (Parameter 'comparer')
What it means
GroupBy's comparer overload requires a non-null IEqualityComparer<TKey> to decide group membership; passing null makes key equality undefined, so the operator throws ArgumentNullException eagerly. Note the default comparer is used automatically when no comparer is supplied.
Solutions
- Pass a concrete comparer such as EqualityComparer<TKey>.Default or StringComparer.OrdinalIgnoreCase.
- Coalesce before calling: comparer ?? EqualityComparer<TKey>.Default.
- Use a GroupBy overload without a comparer to get the default automatically.
Example fix
// before var grouped = source.GroupBy(x => x.Key, x => x, 16, maybeComparer); // after var grouped = source.GroupBy(x => x.Key, x => x, 16, maybeComparer ?? EqualityComparer<TKey>.Default);
Defensive patterns
Strategy: validation
Validate before calling
comparer ??= EqualityComparer<TKey>.Default; var grouped = source.GroupBy(keySelector, elementSelector, capacity, comparer);
Type guard
bool HasComparer<TKey>(IEqualityComparer<TKey>? c) => c is not null;
Try / catch
try
{
grouped = source.GroupBy(keySelector, elementSelector, capacity, comparer);
}
catch (ArgumentNullException ex) when (ex.ParamName == "comparer")
{
grouped = source.GroupBy(keySelector, elementSelector); // default comparer
} Prevention
- Coalesce optional comparers to EqualityComparer<TKey>.Default at the call site.
- Mark comparer parameters non-nullable unless a default is genuinely intended.
- Ensure DI-registered comparer singletons are configured before use.
When it happens
Trigger: Calling AsyncObservable.GroupBy(source, keySelector, elementSelector, capacity, comparer) with comparer == null — e.g. a comparer field/property that was never initialized or a method parameter forwarded as null.
Common situations: DI-resolved or configurable comparer is null; a helper method has a nullable comparer parameter and forwards it directly instead of falling back to EqualityComparer<TKey>.Default.
Related errors
- Value cannot be null. (Parameter 'elementSelector')
- Value cannot be null. (Parameter 'comparer')
- Value cannot be null. (Parameter 'scheduler')
- Value cannot be null. (Parameter 'source')
- Value cannot be null.
AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15).
Data as JSON: /api/errors/178e2500c37db415.
Report an issue: GitHub.
Appendix: source
Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/GroupBy.cs:262
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);
await d.AssignAsync(inner).ConfigureAwait(false);
return subscription;
}View on GitHub (pinned to 94b5d5ab91)