{"record":{"id":"8b7f5892892468d3","repo":"dotnet/reactive","slug":"value-cannot-be-null-parameter-onnextasync","errorCode":null,"errorMessage":"Value cannot be null. (Parameter 'onNextAsync')","messagePattern":"Value cannot be null\\. \\(Parameter 'onNextAsync'\\)","errorType":"validation","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"AsyncRx.NET/System.Reactive.Async/AsyncObservableExtensions.cs","lineNumber":17,"sourceCode":"﻿// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT License.\n// See the LICENSE file in the project root for more information. \n\nusing System.Reactive;\nusing System.Threading.Tasks;\n\nnamespace System\n{\n    public static class AsyncObservableExtensions\n    {\n        public static ValueTask<IAsyncDisposable> SubscribeAsync<T>(this IAsyncObservable<T> source, Func<T, ValueTask> onNextAsync)\n        {\n            if (source == null)\n                throw new ArgumentNullException(nameof(source));\n            if (onNextAsync == null)\n                throw new ArgumentNullException(nameof(onNextAsync));\n\n            return source.SubscribeAsync(new AsyncObserver<T>(onNextAsync, ex => new ValueTask(Task.FromException(ex)), () => default));\n        }\n\n        public static ValueTask<IAsyncDisposable> SubscribeAsync<T>(this IAsyncObservable<T> source, Func<T, ValueTask> onNextAsync, Func<Exception, ValueTask> onErrorAsync)\n        {\n            if (source == null)\n                throw new ArgumentNullException(nameof(source));\n            if (onNextAsync == null)\n                throw new ArgumentNullException(nameof(onNextAsync));\n            if (onErrorAsync == null)\n                throw new ArgumentNullException(nameof(onErrorAsync));\n\n            return source.SubscribeAsync(new AsyncObserver<T>(onNextAsync, onErrorAsync, () => default));\n        }\n\n        public static ValueTask<IAsyncDisposable> SubscribeAsync<T>(this IAsyncObservable<T> source, Func<T, ValueTask> onNextAsync, Func<ValueTask> onCompletedAsync)\n        {","sourceCodeStart":1,"sourceCodeEnd":35,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/AsyncRx.NET/System.Reactive.Async/AsyncObservableExtensions.cs#L1-L35","documentation":"The 4-argument ToLookup overload requires a non-null IEqualityComparer<TKey> because it constructs Lookup<TKey,TValue>(comparer) as the aggregate seed. A null comparer would make bucket lookups undefined, so ArgumentNullException(nameof(comparer)) is thrown before the pipeline is returned (ToLookup.cs:140).","triggerScenarios":"Calling AsyncObservable.ToLookup(observer, keySelector, valueSelector, null); passing a comparer variable resolved from DI/config that is null.","commonSituations":"Comparer chosen based on settings (case-insensitive vs default) with an unhandled else-branch; caching a comparer in a dictionary lookup that missed; custom equality comparer class not registered.","solutions":["Pass a valid IEqualityComparer<TKey> implementation","Omit the comparer argument to use EqualityComparer<TKey>.Default via the 3-argument overload","For case-insensitive keys use StringComparer.OrdinalIgnoreCase or similar"],"exampleFix":"// before\nIEqualityComparer<int> comparer = GetComparer(); // returns null\nvar op = AsyncObservable.ToLookup(sink, x => x.Key, x => x.Value, comparer);\n// after\nvar op = AsyncObservable.ToLookup(sink, x => x.Key, x => x.Value, EqualityComparer<int>.Default);","handlingStrategy":"validation","validationCode":"var cmp = comparer ?? EqualityComparer<TKey>.Default;\nvar op = AsyncObservable.ToLookup(observer, keySelector, valueSelector, cmp);","typeGuard":"static IEqualityComparer<T> OrDefault<T>(IEqualityComparer<T>? c) => c ?? EqualityComparer<T>.Default;","tryCatchPattern":"try\n{\n    var op = AsyncObservable.ToLookup(observer, keySelector, valueSelector, comparer);\n}\ncatch (ArgumentNullException ex) when (ex.ParamName == \"comparer\")\n{\n    var op = AsyncObservable.ToLookup(observer, keySelector, valueSelector); // default comparer\n}","preventionTips":["Coalesce comparer to EqualityComparer<TKey>.Default when optional","Prefer the 3-argument overload when default equality is acceptable","Ensure custom comparers are registered/created before pipeline composition"],"tags":["argument-null","equality-comparer","linq-operator"],"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"}