{"record":{"id":"fd360b1e88812ca3","repo":"dotnet/reactive","slug":"value-cannot-be-null-parameter-observer-groupby","errorCode":null,"errorMessage":"Value cannot be null. (Parameter 'observer')","messagePattern":"Value cannot be null\\. \\(Parameter 'observer'\\)","errorType":"validation","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"AsyncRx.NET/System.Reactive.Async/Linq/Operators/GroupBy.cs","lineNumber":288,"sourceCode":"        private static async ValueTask<IAsyncDisposable> GroupByCore<TSource, TKey, TElement>(IAsyncObservable<TSource> source, IAsyncObserver<IGroupedAsyncObservable<TKey, TElement>> observer, Func<IAsyncObserver<IGroupedAsyncObservable<TKey, TElement>>, IAsyncDisposable, (IAsyncObserver<TSource>, IAsyncDisposable)> createObserver)\n        {\n            var d = new SingleAssignmentAsyncDisposable();\n\n            var (sink, subscription) = createObserver(observer, d);\n\n            var inner = await source.SubscribeSafeAsync(sink).ConfigureAwait(false);\n            await d.AssignAsync(inner).ConfigureAwait(false);\n\n            return subscription;\n        }\n    }\n\n    public partial class AsyncObserver\n    {\n        public static (IAsyncObserver<TSource>, IAsyncDisposable) GroupBy<TSource, TKey>(IAsyncObserver<IGroupedAsyncObservable<TKey, TSource>> observer, IAsyncDisposable subscription, Func<TSource, 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, 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));","sourceCodeStart":270,"sourceCodeEnd":306,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/AsyncRx.NET/System.Reactive.Async/Linq/Operators/GroupBy.cs#L270-L306","documentation":"AsyncObserver.GroupBy is the internal factory that builds the observer tuple driving the GroupBy pipeline. It validates observer first and throws ArgumentNullException when it is null. This overload is normally invoked by the public GroupBy operator and by GroupByCore, so hitting it usually means an observer pipeline was assembled manually with a missing observer.","triggerScenarios":"Calling AsyncObserver.GroupBy<TSource, TKey>(observer, subscription, keySelector) directly (or via a custom operator builder) with a null IAsyncObserver<IGroupedAsyncObservable<TKey, TSource>>.","commonSituations":"Hand-rolling custom async operators on top of AsyncObserver.*; a CreateAsyncObservable callback receiving and forwarding a null observer; wiring the observer tuple in the wrong order so null lands in the observer slot.","solutions":["Pass the real IAsyncObserver instance obtained from CreateAsyncObservable.From's callback; do not construct the observer pipeline with null.","If building a custom operator, ensure your observerFactory returns a valid observer before calling AsyncObserver.GroupBy.","Guard with a null check on the observer before invoking the factory."],"exampleFix":"// before\nreturn AsyncObserver.GroupBy(null, subscription, keySelector);\n// after\nif (observer == null) throw new ArgumentNullException(nameof(observer));\nreturn AsyncObserver.GroupBy(observer, subscription, keySelector);","handlingStrategy":"validation","validationCode":"if (observer == null) throw new ArgumentNullException(nameof(observer));\nvar (o, d) = AsyncObserver.GroupBy(observer, subscription, keySelector);","typeGuard":"bool CanBuildGroupBy<TSource, TKey>(IAsyncObserver<IGroupedAsyncObservable<TKey, TSource>>? observer) => observer is not null;","tryCatchPattern":"try\n{\n    var (o, d) = AsyncObserver.GroupBy(observer, subscription, keySelector);\n}\ncatch (ArgumentNullException ex) when (ex.ParamName == \"observer\")\n{\n    throw new InvalidOperationException(\"GroupBy requires a non-null observer from the operator callback.\", ex);\n}","preventionTips":["Only call AsyncObserver.* factories from inside CreateAsyncObservable.From callbacks where the observer is guaranteed.","Use static lambdas with state tuples as the library does to avoid capturing nulls.","Unit-test custom operators with a recording IAsyncObserver stub."],"tags":["argument-null","observer","asyncobserver","asyncrx"],"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"}