dotnet/reactive · error · ArgumentNullException

observer

Error message

observer

What it means

GroupBy throws ArgumentNullException because the required observer (downstream IAsyncObserver<IGroupedAsyncObservable<TKey,TSource>>) is null. The operator validates its core arguments eagerly so an invalid subscription fails immediately rather than when events flow. It is part of the standard null-guard pattern in AsyncRx.NET operator entry points.

Solutions

  1. Ensure the IAsyncObserver<IGroupedAsyncObservable<TKey,TSource>> passed to GroupBy is non-null; construct it with a factory such as AsyncObserver.Create<TSource>(...).
  2. Check the argument order: the observer is the first parameter, the subscription the second — swapped arguments commonly make the observer null.
  3. Verify any variable holding the observer is initialized before GroupBy is invoked (defer construction until both observer and subscription exist).
  4. If the observer comes from another operator, confirm that operator's return value is not null on the code path taken.

Example fix

// before
var grp = GroupBy<TSource, TKey>(observer, subscription, x => x.Key); // observer is null
// after
var obs = AsyncObserver.Create<TSource>(onNextAsync: x => default(ValueTask));
var grp = GroupBy<TSource, TKey>(obs, subscription, x => x.Key);
Defensive patterns

Strategy: validation

Validate before calling

if (observer == null) throw new InvalidOperationException("observer must be provided before GroupBy");
var grp = GroupBy<TSource, TKey>(observer, subscription, keySelector);

Type guard

bool IsValidGroupByArgs<TSource, TKey>(IAsyncObserver<IGroupedAsyncObservable<TKey, TSource>> o) => o is not null;

Try / catch

try { var grp = GroupBy<TSource, TKey>(observer, subscription, keySelector); }
catch (ArgumentNullException ex) when (ex.ParamName == "observer") { /* fix pipeline wiring / log */ }

Prevention

When it happens

Trigger: Calling the 2-type-parameter GroupBy<TSource,TKey>(observer, subscription, keySelector) overload with a null observer, i.e. AsyncRx.NET.Linq.Operators.GroupBy(null, subscription, keySelector) or wiring a subscription whose observer was never created.

Common situations: Manually building operator chains where the observer is produced by a factory that returned null; passing a field that is not yet initialized because the observable pipeline was constructed before its observer; refactorings that swap the observer/subscription argument order.

Related errors


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

Appendix: source

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

                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 (capacity < 0)
                throw new ArgumentOutOfRangeException(nameof(capacity));
            if (comparer == null)
                throw new ArgumentNullException(nameof(comparer));

            return GroupBy<TSource, TKey, TElement>(observer, subscription, x => new ValueTask<TKey>(keySelector(x)), x => new ValueTask<TElement>(elementSelector(x)), capacity, comparer);
        }

        public static (IAsyncObserver<TSource>, IAsyncDisposable) GroupBy<TSource, TKey>(IAsyncObserver<IGroupedAsyncObservable<TKey, TSource>> observer, IAsyncDisposable subscription, Func<TSource, ValueTask<TKey>> keySelector)
        {
            if (observer == null)
                throw new ArgumentNullException(nameof(observer));
            if (subscription == null)
                throw new ArgumentNullException(nameof(subscription));
            if (keySelector == null)
                throw new ArgumentNullException(nameof(keySelector));

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

        public static (IAsyncObserver<TSource>, IAsyncDisposable) GroupBy<TSource, TKey>(IAsyncObserver<IGroupedAsyncObservable<TKey, TSource>> observer, IAsyncDisposable subscription, Func<TSource, ValueTask<TKey>> keySelector, 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 (comparer == null)
                throw new ArgumentNullException(nameof(comparer));

View on GitHub (pinned to 94b5d5ab91)