{"record":{"id":"e4531eb96dcf6f89","repo":"dotnet/reactive","slug":"argumentnullexception-observer","errorCode":null,"errorMessage":"ArgumentNullException: observer","messagePattern":"ArgumentNullException: observer","errorType":"validation","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"AsyncRx.NET/System.Reactive.Async/Linq/Operators/GroupByUntil.cs","lineNumber":368,"sourceCode":"        {\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));\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));","sourceCodeStart":350,"sourceCodeEnd":386,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/AsyncRx.NET/System.Reactive.Async/Linq/Operators/GroupByUntil.cs#L350-L386","documentation":"The full-overload GroupByUntil(observer, subscription, keySelector, durationSelector, capacity, comparer) (GroupByUntil.cs:365) first validates its observer argument — the IAsyncObserver that receives the grouped observables — and throws ArgumentNullException when it is null. Without an observer there is nowhere to deliver group results, so the call is rejected up front.","triggerScenarios":"Invoking the internal/operator surface of GroupByUntil with observer == null; in practice this happens in custom pipeline code or operator composition where the downstream observer is a nullable field or the result of a failed lookup.","commonSituations":"Writing custom operators that forward a downstream observer which can be null before subscription completes; unit tests constructing the operator manually; DI containers producing null observer instances.","solutions":["Supply a valid IAsyncObserver<IGroupedAsyncObservable<TKey, TSource>> to the operator","Check where the observer comes from — ensure the subscription/pipeline was fully initialized before calling GroupByUntil","If writing a custom operator, throw or defer subscription when the downstream observer is null instead of forwarding it"],"exampleFix":"// before\nawait AsyncObservable.GroupByUntil(null, subscription, k => k, g => g.Take(1), 8, cmp);\n// after\nif (observer == null) throw new InvalidOperationException(\"observer not initialized\");\nawait AsyncObservable.GroupByUntil(observer, subscription, k => k, g => g.Take(1), 8, cmp);","handlingStrategy":"validation","validationCode":"if (observer == null) throw new InvalidOperationException(\"Downstream observer must be initialized before applying GroupByUntil\");","typeGuard":"bool HasObserver<TSource>(IAsyncObserver<TSource>? o) => o is not null;","tryCatchPattern":"try { var res = AsyncObservable.GroupByUntil(observer, subscription, keySelector, durationSelector, capacity, comparer); }\ncatch (ArgumentNullException ex) when (ex.ParamName == \"observer\") { /* re-create or await the pipeline observer */ }","preventionTips":["Apply operators only inside a fully constructed subscription pipeline","Avoid storing downstream observers in nullable fields read lazily","Cover custom operator composition with integration tests through Subscribe"],"tags":["csharp","argument-null","async-rx","groupbyuntil"],"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"}