{"record":{"id":"514e681f808e2f6b","repo":"dotnet/reactive","slug":"keyselector-groupbyuntil","errorCode":null,"errorMessage":"keySelector","messagePattern":"keySelector","errorType":"validation","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"AsyncRx.NET/System.Reactive.Async/Linq/Operators/GroupByUntil.cs","lineNumber":21,"sourceCode":"// See the LICENSE file in the project root for more information. \n\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Reactive.Disposables;\nusing System.Reactive.Subjects;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace System.Reactive.Linq\n{\n    public partial class AsyncObservable\n    {\n        public static IAsyncObservable<IGroupedAsyncObservable<TKey, TSource>> GroupByUntil<TSource, TKey, TDuration>(this IAsyncObservable<TSource> source, Func<TSource, TKey> keySelector, Func<IGroupedAsyncObservable<TKey, TSource>, IAsyncObservable<TDuration>> durationSelector)\n        {\n            if (source == null)\n                throw new ArgumentNullException(nameof(source));\n            if (keySelector == null)\n                throw new ArgumentNullException(nameof(keySelector));\n            if (durationSelector == null)\n                throw new ArgumentNullException(nameof(durationSelector));\n\n            return CreateAsyncObservable<IGroupedAsyncObservable<TKey, TSource>>.From(\n                source,\n                (keySelector, durationSelector),\n                static (source, state, observer) => GroupByUntilCore<TSource, TKey, TSource, TDuration>(source, observer, (o, d) => AsyncObserver.GroupByUntil(o, d, state.keySelector, state.durationSelector)));\n        }\n\n        public static IAsyncObservable<IGroupedAsyncObservable<TKey, TSource>> GroupByUntil<TSource, TKey, TDuration>(this IAsyncObservable<TSource> source, Func<TSource, TKey> keySelector, Func<IGroupedAsyncObservable<TKey, TSource>, IAsyncObservable<TDuration>> durationSelector, IEqualityComparer<TKey> comparer)\n        {\n            if (source == null)\n                throw new ArgumentNullException(nameof(source));\n            if (keySelector == null)\n                throw new ArgumentNullException(nameof(keySelector));\n            if (durationSelector == null)\n                throw new ArgumentNullException(nameof(durationSelector));\n            if (comparer == null)","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/AsyncRx.NET/System.Reactive.Async/Linq/Operators/GroupByUntil.cs#L3-L39","documentation":"AsyncObservable.GroupByUntil (2-argument overload) throws ArgumentNullException when the keySelector delegate is null. The key selector assigns each element to a group and is captured in the operator state, so it must be provided. This guard executes synchronously when the operator is constructed.","triggerScenarios":"Calling GroupByUntil with a null Func<TSource, TKey>, e.g. when the selector is supplied by a caller of a wrapper method that did not validate, or an expression tree converted to null.","commonSituations":"Public wrapper APIs forwarding an optional keySelector without checks; lambda built from reflection that failed; copied code where the selector line was deleted.","solutions":["Pass a concrete key selector such as x => x.GroupKey.","Validate delegates in your wrapper methods before forwarding.","Avoid optional key-selector parameters; require them in the signature."],"exampleFix":"// before\npublic static IOpped Group<T>(IAsyncObservable<T> s, Func<T, string> k = null)\n    => s.GroupByUntil(k, g => g.Take(1));\n// after\npublic static IOpped Group<T>(IAsyncObservable<T> s, Func<T, string> k)\n    => s.GroupByUntil(k, g => g.Take(1));","handlingStrategy":"validation","validationCode":"if (keySelector is null) throw new ArgumentNullException(nameof(keySelector));","typeGuard":"bool HasKey<T,TKey>(Func<T,TKey>? k) => k is not null;","tryCatchPattern":"try { var grouped = source.GroupByUntil(k, d); }\ncatch (ArgumentNullException ex) when (ex.ParamName == \"keySelector\") { /* supply key selector */ }","preventionTips":["Require key selectors as mandatory parameters in wrapper APIs.","Never forward optional delegates without a null check.","Test operator wrappers with null delegates to confirm guard behavior."],"tags":["asyncrx","null-argument","groupbyuntil","delegate"],"backgroundTag":"null-argument","analyzedSha":"94b5d5ab912789f5abe9a72138a25bbd716fe59c","analyzedAt":"2026-09-15T02:26:24.759Z","contentChangedAt":"2026-09-15T02:26:24.759Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}