dotnet/reactive · error · ArgumentNullException

subscription

Error message

subscription

What it means

GroupBy throws ArgumentNullException because the required subscription (IAsyncDisposable for the upstream subscription) is null. The operator needs this disposable to propagate cancellation/disposal through the chain. Like the other guards it fails fast at operator-construction time.

Solutions

  1. Pass a valid IAsyncDisposable as the second argument; if there is nothing to dispose, use a no-op such as AsyncDisposable.Nop (or an empty disposable).
  2. Capture the disposable returned by the upstream operator's Subscribe and forward it into GroupBy.
  3. Check argument order — subscription is parameter 2, after observer.
  4. Audit custom operator plumbing so every operator in the chain passes the subscription token along.

Example fix

// before
var grp = GroupBy<TSource, TKey>(observer, subscription, x => x.Key); // subscription is null
// after
subscription = AsyncDisposable.Nop;
var grp = GroupBy<TSource, TKey>(observer, subscription, x => x.Key);
Defensive patterns

Strategy: validation

Validate before calling

if (subscription == null) subscription = AsyncDisposable.Nop;
var grp = GroupBy<TSource, TKey>(observer, subscription, keySelector);

Type guard

bool HasSubscription(IAsyncDisposable d) => d is not null;

Try / catch

try { var grp = GroupBy<TSource, TKey>(observer, subscription, keySelector); }
catch (ArgumentNullException ex) when (ex.ParamName == "subscription") { /* supply/propagate the disposable */ }

Prevention

When it happens

Trigger: Calling the GroupBy<TSource,TKey>(observer, subscription, keySelector) overload with a null subscription, e.g. GroupBy(observer, null, keySelector), typically when the upstream subscription disposable was never captured.

Common situations: Hand-rolled subscription wiring where the upstream Dispose handle is dropped; composing operators in a helper that forgets to thread the IAsyncDisposable through; disposing logic refactored so the disposable becomes null.

Related errors


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

Appendix: source

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

                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));

            return GroupBy(observer, subscription, keySelector, int.MaxValue, comparer);

View on GitHub (pinned to 94b5d5ab91)