{"record":{"id":"66ec347408ee1c34","repo":"dotnet/reactive","slug":"argumentnullexception-keyselector-groupbyuntil","errorCode":null,"errorMessage":"ArgumentNullException: keySelector","messagePattern":"ArgumentNullException: keySelector","errorType":"validation","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"AsyncRx.NET/System.Reactive.Async/Linq/Operators/GroupByUntil.cs","lineNumber":356,"sourceCode":"                throw new ArgumentNullException(nameof(subscription));\n            if (keySelector == null)\n                throw new ArgumentNullException(nameof(keySelector));\n            if (durationSelector == null)\n                throw new ArgumentNullException(nameof(durationSelector));\n            if (comparer == null)\n                throw new ArgumentNullException(nameof(comparer));\n\n            return GroupByUntil(observer, subscription, keySelector, durationSelector, int.MaxValue, comparer);\n        }\n\n        public static ValueTask<(IAsyncObserver<TSource>, IAsyncDisposable)> GroupByUntil<TSource, TKey, TDuration>(IAsyncObserver<IGroupedAsyncObservable<TKey, TSource>> observer, IAsyncDisposable subscription, Func<TSource, TKey> keySelector, Func<IGroupedAsyncObservable<TKey, TSource>, IAsyncObservable<TDuration>> durationSelector, int capacity)\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 (durationSelector == null)\n                throw new ArgumentNullException(nameof(durationSelector));\n            if (capacity < 0)\n                throw new ArgumentOutOfRangeException(nameof(capacity));\n\n            return GroupByUntil(observer, subscription, keySelector, durationSelector, capacity, EqualityComparer<TKey>.Default);\n        }\n\n        public static ValueTask<(IAsyncObserver<TSource>, IAsyncDisposable)> GroupByUntil<TSource, TKey, TDuration>(IAsyncObserver<IGroupedAsyncObservable<TKey, TSource>> observer, IAsyncDisposable subscription, Func<TSource, TKey> keySelector, Func<IGroupedAsyncObservable<TKey, TSource>, IAsyncObservable<TDuration>> durationSelector, int capacity, 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 (durationSelector == null)\n                throw new ArgumentNullException(nameof(durationSelector));","sourceCodeStart":338,"sourceCodeEnd":374,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/AsyncRx.NET/System.Reactive.Async/Linq/Operators/GroupByUntil.cs#L338-L374","documentation":"Thrown when keySelector is null in the capacity overload of GroupByUntil (core overload with int capacity). As in every other GroupByUntil overload, the key extraction delegate is mandatory and validated eagerly at the call site.","triggerScenarios":"Calling GroupByUntil(observer, subscription, null, durationSelector, capacity) or forwarding a null selector from a wrapper.","commonSituations":"DI-resolved or config-built key selectors that came out null; wrappers that do not re-validate user-supplied delegates before delegating to the core overload.","solutions":["Supply a non-null keySelector delegate","Add validation in your wrapper before calling the core overload","Fix the upstream factory/lookup that returned null"],"exampleFix":"// before\nvar grouped = GroupByUntil(obs, sub, selectors.Key /* null */, ds, 16);\n// after\nvar sel = selectors.Key ?? throw new ArgumentNullException(nameof(selectors.Key));\nvar grouped = GroupByUntil(obs, sub, sel, ds, 16);","handlingStrategy":"validation","validationCode":"if (keySelector == null) throw new ArgumentNullException(nameof(keySelector));","typeGuard":"bool IsValidSelector<TSource,TKey>(Func<TSource,TKey> f) => f is not null;","tryCatchPattern":"try\n{\n    var result = await GroupByUntil(observer, subscription, ks, ds, capacity);\n}\ncatch (ArgumentNullException ex) when (ex.ParamName == \"keySelector\")\n{\n    // fix selector wiring upstream\n}","preventionTips":["Validate delegates at pipeline-builder boundaries","Never store selectors in nullable fields without defaults","Resolve selectors from DI with non-null registrations"],"tags":["argument-null","linq-operator","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"}