{"record":{"id":"1bf56c2ee7b65547","repo":"dotnet/reactive","slug":"value-cannot-be-null-parameter-oncompleted-observable","errorCode":null,"errorMessage":"Value cannot be null. (Parameter 'onCompleted')","messagePattern":"Value cannot be null\\. \\(Parameter 'onCompleted'\\)","errorType":"validation","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"Rx.NET/Source/src/System.Reactive/Linq/Observable.StandardSequenceOperators.cs","lineNumber":1390,"sourceCode":"        {\n            if (source == null)\n            {\n                throw new ArgumentNullException(nameof(source));\n            }\n\n            if (onNext == null)\n            {\n                throw new ArgumentNullException(nameof(onNext));\n            }\n\n            if (onError == null)\n            {\n                throw new ArgumentNullException(nameof(onError));\n            }\n\n            if (onCompleted == null)\n            {\n                throw new ArgumentNullException(nameof(onCompleted));\n            }\n\n            return s_impl.SelectMany(source, onNext, onError, onCompleted);\n        }\n\n        /// <summary>\n        /// Projects each notification of an observable sequence to an observable sequence by incorporating the element's index and merges the resulting observable sequences into one observable sequence.\n        /// </summary>\n        /// <typeparam name=\"TSource\">The type of the elements in the source sequence.</typeparam>\n        /// <typeparam name=\"TResult\">The type of the elements in the projected inner sequences and the elements in the merged result sequence.</typeparam>\n        /// <param name=\"source\">An observable sequence of notifications to project.</param>\n        /// <param name=\"onNext\">A transform function to apply to each element; the second parameter of the function represents the index of the source element.</param>\n        /// <param name=\"onError\">A transform function to apply when an error occurs in the source sequence.</param>\n        /// <param name=\"onCompleted\">A transform function to apply when the end of the source sequence is reached.</param>\n        /// <returns>An observable sequence whose elements are the result of invoking the one-to-many transform function corresponding to each notification in the input sequence.</returns>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"source\"/> or <paramref name=\"onNext\"/> or <paramref name=\"onError\"/> or <paramref name=\"onCompleted\"/> is null.</exception>\n        public static IObservable<TResult> SelectMany<TSource, TResult>(this IObservable<TSource> source, Func<TSource, int, IObservable<TResult>> onNext, Func<Exception, IObservable<TResult>> onError, Func<IObservable<TResult>> onCompleted)\n        {","sourceCodeStart":1372,"sourceCodeEnd":1408,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/Rx.NET/Source/src/System.Reactive/Linq/Observable.StandardSequenceOperators.cs#L1372-L1408","documentation":"Thrown by the notification-based SelectMany(source, onNext, onError, onCompleted) overload when the onCompleted delegate (Func<IObservable<TResult>>) is null. Completion must be mapped to a terminating result sequence, so Rx validates it eagerly and rejects null before any subscription.","triggerScenarios":"Calling Observable.SelectMany(source, onNext, onError, null) — the completion-to-observable factory is null, typically when the developer supplies handlers positionally and stops early.","commonSituations":"Incomplete refactors adding notification-based SelectMany over an existing two-argument version; misunderstanding that onCompleted is optional; template code copied partially from documentation.","solutions":["Pass a completion factory, e.g. () => Observable.Empty<TResult>(), as the standard completion mapping.","If completion should emit a final value, use () => Observable.Return(defaultValue).","Switch to a simpler SelectMany overload if per-notification mapping is not actually needed."],"exampleFix":"// before\nvar q = source.SelectMany(x => Load(x), ex => Observable.Throw<Item>(ex), null);\n\n// after\nvar q = source.SelectMany(x => Load(x), ex => Observable.Throw<Item>(ex), () => Observable.Empty<Item>());","handlingStrategy":"validation","validationCode":"if (onCompleted is null) onCompleted = static () => Observable.Empty<TResult>(); // or throw before the call","typeGuard":"static bool CompletionFactoryPresent<TResult>(Func<IObservable<TResult>> onCompleted)\n    => onCompleted is not null;","tryCatchPattern":"try\n{\n    var q = source.SelectMany(onNext, onError, onCompleted);\n}\ncatch (ArgumentNullException ex) when (ex.ParamName == \"onCompleted\")\n{\n    q = source.SelectMany(onNext, onError, static () => Observable.Empty<TResult>());\n}","preventionTips":["Always pass () => Observable.Empty<TResult>() unless completion must emit a value.","Review all positional argument lists ending in null after refactors.","Centralize notification-handler triples in reusable helper methods to avoid omissions."],"tags":["null-argument","argument-validation","reactive-extensions","linq"],"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"}