dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'comparer')

Error message

Value cannot be null. (Parameter 'comparer')

What it means

The comparer overload of GroupByUntil requires a non-null IEqualityComparer<TKey> used to bucket elements into groups. A null comparer throws ArgumentNullException at call time in the guard at GroupByUntil.cs:40. Pass EqualityComparer<TKey>.Default when no custom comparison is needed.

Solutions

  1. Use EqualityComparer<TKey>.Default in place of null
  2. Ensure the configured/custom comparer is instantiated before the call
  3. Drop the comparer argument entirely and use the (source, keySelector, durationSelector) overload

Example fix

// before
var groups = source.GroupByUntil(x => x.Name, g => g.Take(1), GetComparerOrNull());
// after
var cmp = GetComparerOrNull() ?? EqualityComparer<string>.Default;
var groups = source.GroupByUntil(x => x.Name, g => g.Take(1), cmp);
Defensive patterns

Strategy: validation

Validate before calling

var cmp2 = comparer ?? EqualityComparer<TKey>.Default;

Type guard

bool HasComparer<TKey>(IEqualityComparer<TKey> c) => c is not null;

Try / catch

try { var groups = source.GroupByUntil(ks, ds, cmp); } catch (ArgumentNullException ex) when (ex.ParamName == "comparer") { var groups = source.GroupByUntil(ks, ds, EqualityComparer<TKey>.Default); }

Prevention

When it happens

Trigger: Calling AsyncObservable.GroupByUntil(source, keySelector, durationSelector, (IEqualityComparer<TKey>)null), typically when the comparer comes from an optional settings object that was not populated.

Common situations: Custom comparers injected via DI or configuration returning null when unset, caching an EqualityComparer lookup that missed, or case-insensitive key comparison setup forgotten.

Related errors


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

Appendix: source

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

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

            return CreateAsyncObservable<IGroupedAsyncObservable<TKey, TSource>>.From(
                source,
                (keySelector, durationSelector),
                static (source, state, observer) => GroupByUntilCore<TSource, TKey, TSource, TDuration>(source, observer, (o, d) => AsyncObserver.GroupByUntil(o, d, state.keySelector, state.durationSelector)));
        }

        public static IAsyncObservable<IGroupedAsyncObservable<TKey, TSource>> GroupByUntil<TSource, TKey, TDuration>(this IAsyncObservable<TSource> source, Func<TSource, TKey> keySelector, Func<IGroupedAsyncObservable<TKey, TSource>, IAsyncObservable<TDuration>> durationSelector, IEqualityComparer<TKey> comparer)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            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 CreateAsyncObservable<IGroupedAsyncObservable<TKey, TSource>>.From(
                source,
                (keySelector, durationSelector, comparer),
                static (source, state, observer) => GroupByUntilCore<TSource, TKey, TSource, TDuration>(source, observer, (o, d) => AsyncObserver.GroupByUntil(o, d, state.keySelector, state.durationSelector, state.comparer)));
        }

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

View on GitHub (pinned to 94b5d5ab91)