{"record":{"id":"8cd15a98318e61f1","repo":"dotnet/reactive","slug":"subscription","errorCode":null,"errorMessage":"subscription","messagePattern":"subscription","errorType":"validation","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"AsyncRx.NET/System.Reactive.Async/Linq/Operators/GroupBy.cs","lineNumber":410,"sourceCode":"                throw new ArgumentNullException(nameof(subscription));\n            if (keySelector == null)\n                throw new ArgumentNullException(nameof(keySelector));\n            if (elementSelector == null)\n                throw new ArgumentNullException(nameof(elementSelector));\n            if (capacity < 0)\n                throw new ArgumentOutOfRangeException(nameof(capacity));\n            if (comparer == null)\n                throw new ArgumentNullException(nameof(comparer));\n\n            return GroupBy<TSource, TKey, TElement>(observer, subscription, x => new ValueTask<TKey>(keySelector(x)), x => new ValueTask<TElement>(elementSelector(x)), capacity, comparer);\n        }\n\n        public static (IAsyncObserver<TSource>, IAsyncDisposable) GroupBy<TSource, TKey>(IAsyncObserver<IGroupedAsyncObservable<TKey, TSource>> observer, IAsyncDisposable subscription, Func<TSource, ValueTask<TKey>> keySelector)\n        {\n            if (observer == null)\n                throw new ArgumentNullException(nameof(observer));\n            if (subscription == null)\n                throw new ArgumentNullException(nameof(subscription));\n            if (keySelector == null)\n                throw new ArgumentNullException(nameof(keySelector));\n\n            return GroupBy(observer, subscription, keySelector, int.MaxValue, EqualityComparer<TKey>.Default);\n        }\n\n        public static (IAsyncObserver<TSource>, IAsyncDisposable) GroupBy<TSource, TKey>(IAsyncObserver<IGroupedAsyncObservable<TKey, TSource>> observer, IAsyncDisposable subscription, Func<TSource, ValueTask<TKey>> keySelector, IEqualityComparer<TKey> comparer)\n        {\n            if (observer == null)\n                throw new ArgumentNullException(nameof(observer));\n            if (subscription == null)\n                throw new ArgumentNullException(nameof(subscription));\n            if (keySelector == null)\n                throw new ArgumentNullException(nameof(keySelector));\n            if (comparer == null)\n                throw new ArgumentNullException(nameof(comparer));\n\n            return GroupBy(observer, subscription, keySelector, int.MaxValue, comparer);","sourceCodeStart":392,"sourceCodeEnd":428,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/AsyncRx.NET/System.Reactive.Async/Linq/Operators/GroupBy.cs#L392-L428","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nvar grp = GroupBy<TSource, TKey>(observer, subscription, x => x.Key); // subscription is null\n// after\nsubscription = AsyncDisposable.Nop;\nvar grp = GroupBy<TSource, TKey>(observer, subscription, x => x.Key);","handlingStrategy":"validation","validationCode":"if (subscription == null) subscription = AsyncDisposable.Nop;\nvar grp = GroupBy<TSource, TKey>(observer, subscription, keySelector);","typeGuard":"bool HasSubscription(IAsyncDisposable d) => d is not null;","tryCatchPattern":"try { var grp = GroupBy<TSource, TKey>(observer, subscription, keySelector); }\ncatch (ArgumentNullException ex) when (ex.ParamName == \"subscription\") { /* supply/propagate the disposable */ }","preventionTips":["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."],"tags":["argument-null","reactive-extensions","groupby","csharp"],"backgroundTag":"null-argument","analyzedSha":"94b5d5ab912789f5abe9a72138a25bbd716fe59c","analyzedAt":"2026-09-15T02:26:24.759Z","contentChangedAt":"2026-09-15T02:26:24.759Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}