{"record":{"id":"b25a3795743ad88a","repo":"dotnet/reactive","slug":"argumentnullexception-elementselector-groupbyuntil","errorCode":null,"errorMessage":"ArgumentNullException: elementSelector","messagePattern":"ArgumentNullException: elementSelector","errorType":"validation","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"AsyncRx.NET/System.Reactive.Async/Linq/Operators/GroupByUntil.cs","lineNumber":392,"sourceCode":"                throw new ArgumentNullException(nameof(durationSelector));\n            if (capacity < 0)\n                throw new ArgumentOutOfRangeException(nameof(capacity));\n            if (comparer == null)\n                throw new ArgumentNullException(nameof(comparer));\n\n            return GroupByUntil(observer, subscription, x => new ValueTask<TKey>(keySelector(x)), durationSelector, capacity, comparer);\n        }\n\n        public static ValueTask<(IAsyncObserver<TSource>, IAsyncDisposable)> GroupByUntil<TSource, TKey, TElement, TDuration>(IAsyncObserver<IGroupedAsyncObservable<TKey, TElement>> observer, IAsyncDisposable subscription, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<IGroupedAsyncObservable<TKey, TElement>, IAsyncObservable<TDuration>> durationSelector)\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 (elementSelector == null)\n                throw new ArgumentNullException(nameof(elementSelector));\n            if (durationSelector == null)\n                throw new ArgumentNullException(nameof(durationSelector));\n\n            return GroupByUntil(observer, subscription, keySelector, elementSelector, durationSelector, int.MaxValue, EqualityComparer<TKey>.Default);\n        }\n\n        public static ValueTask<(IAsyncObserver<TSource>, IAsyncDisposable)> GroupByUntil<TSource, TKey, TElement, TDuration>(IAsyncObserver<IGroupedAsyncObservable<TKey, TElement>> observer, IAsyncDisposable subscription, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<IGroupedAsyncObservable<TKey, TElement>, IAsyncObservable<TDuration>> durationSelector, 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 (elementSelector == null)\n                throw new ArgumentNullException(nameof(elementSelector));\n            if (durationSelector == null)\n                throw new ArgumentNullException(nameof(durationSelector));","sourceCodeStart":374,"sourceCodeEnd":410,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/AsyncRx.NET/System.Reactive.Async/Linq/Operators/GroupByUntil.cs#L374-L410","documentation":"GroupByUntil throws ArgumentNullException when elementSelector is null. The elementSelector projects each source element into the element emitted by the grouped observable; without it the operator cannot construct groups. The library validates arguments eagerly so the failure points at the caller rather than inside the async pipeline.","triggerScenarios":"Calling a public GroupByUntil overload at GroupByUntil.cs:392 (the 3-delegate overload forwarding to the full overload with int.MaxValue capacity and default comparer) with null as the elementSelector argument.","commonSituations":"Passing null instead of an identity lambda (o => o) when no element projection is needed; a projection delegate variable left null after conditional setup; misuse of an overload whose parameter order was misread.","solutions":["Pass an identity projection if no transformation is needed: o => o.","Supply the actual projection lambda, e.g. o => o.Lines.","Default an optional selector: elementSelector ?? (x => x).","Verify parameter order — keySelector, elementSelector, durationSelector — in the overload you call."],"exampleFix":"// before\nvar groups = source.GroupByUntil(o => o.Type, null, g => g.TakeUntil(done));\n// after\nvar groups = source.GroupByUntil(o => o.Type, o => o, g => g.TakeUntil(done));","handlingStrategy":"validation","validationCode":"if (elementSelector == null) throw new InvalidOperationException(\"GroupByUntil requires a non-null elementSelector; use o => o for identity\");\nvar groups = source.GroupByUntil(keySelector, elementSelector, durationSelector);","typeGuard":"static bool IsValidSelector<TSource,TElement>(Func<TSource,TElement> f) => f is not null;","tryCatchPattern":"try\n{\n    var groups = source.GroupByUntil(keySelector, elementSelector, durationSelector);\n}\ncatch (ArgumentNullException ex) when (ex.ParamName == \"elementSelector\")\n{\n    // substitute identity projection and retry\n}","preventionTips":["Use o => o explicitly when no projection is needed — never null.","Memorize the parameter order: keySelector, elementSelector, durationSelector.","Coalesce optional projections: elementSelector ?? (x => x).","Review call sites after overload refactors."],"tags":["argument-null","linq","reactive-extensions","groupby"],"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"}