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
- 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).
- Capture the disposable returned by the upstream operator's Subscribe and forward it into GroupBy.
- Check argument order — subscription is parameter 2, after observer.
- 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
- Thread the IAsyncDisposable returned by each operator's Subscribe into the next operator call.
- Use AsyncDisposable.Nop as the canonical 'nothing to dispose' value instead of null.
- In tests, make fake subscriptions return non-null disposables.
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)