{"record":{"id":"a71d61465f60f6ec","repo":"dotnet/reactive","slug":"the-handler-returned-a-null-iobservable-retrywhen","errorCode":null,"errorMessage":"The handler returned a null IObservable","messagePattern":"The handler returned a null IObservable","errorType":"exception","errorClass":"NullReferenceException","httpStatus":null,"severity":"error","filePath":"Rx.NET/Source/src/System.Reactive/Linq/Observable/RetryWhen.cs","lineNumber":41,"sourceCode":"        public IDisposable Subscribe(IObserver<T> observer)\n        {\n            if (observer == null)\n            {\n                throw new ArgumentNullException(nameof(observer));\n            }\n\n            var errorSignals = new Subject<Exception>();\n            \n            IObservable<U> redo;\n\n            try\n            {\n                redo = _handler(errorSignals);\n\n                if (redo == null)\n                {\n#pragma warning disable CA2201 // (Do not raise reserved exception types.) Backwards compatibility prevents us from complying.\n                    throw new NullReferenceException(\"The handler returned a null IObservable\");\n#pragma warning restore CA2201\n                }\n            }\n            catch (Exception ex)\n            {\n                observer.OnError(ex);\n                return Disposable.Empty;\n            }\n\n            var parent = new MainObserver(observer, _source, new RedoSerializedObserver<Exception>(errorSignals));\n\n            var d = redo.SubscribeSafe(parent.HandlerConsumer);\n            parent.HandlerUpstream.Disposable = d;\n\n            parent.HandlerNext();\n\n            return parent;\n        }","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/Rx.NET/Source/src/System.Reactive/Linq/Observable/RetryWhen.cs#L23-L59","documentation":"RetryWhen passes a subject of error signals to the user handler and expects a non-null IObservable<U> controlling resubscription after errors. A null return value is converted into NullReferenceException(\"The handler returned a null IObservable\") delivered via OnError, preserving legacy exception typing (CA2201 suppressed).","triggerScenarios":"source.RetryWhen(errorSignals => ...) where the lambda returns null (e.g. a switch expression without a matching case, or a dictionary lookup miss).","commonSituations":"Retry policy registries keyed by exception type with no default entry; refactored handlers whose return path became null; conditional retry logic that forgot a terminal branch.","solutions":["Always return a valid IObservable from the handler (e.g. the error stream mapped to resubscribe signals, or Observable.Empty<U>() to stop retrying).","Guard in the handler: substitute Observable.Empty<U>() when no policy matches.","Handle OnError in the subscription to surface which handler misbehaved."],"exampleFix":"// before\nsource.RetryWhen(errors => errors.SelectMany(e => policies[e.Type])); // missing policy returns null\n// after\nsource.RetryWhen(errors => errors.SelectMany(e => policies.TryGetValue(e.Type, out var p) ? p : Observable.Empty<Unit>()));","handlingStrategy":"validation","validationCode":"// C# - ensure handler never returns null\nIObservable<Unit> redo = errorSignals.SelectMany(e => GetPolicy(e) ?? Observable.Empty<Unit>());\nsource.RetryWhen(_ => redo).Subscribe(...);","typeGuard":"// C#\nstatic bool IsValidRedo<U>(IObservable<U> o) => o is not null;","tryCatchPattern":"source.RetryWhen(errors => handler(errors)).Subscribe(\n    onNext,\n    ex => { if (ex is NullReferenceException && ex.Message.Contains(\"null IObservable\")) LogHandlerBug(); else throw ex; });","preventionTips":["Provide a default retry policy so lookups never yield null.","Return Observable.Empty<U>() from handlers to terminate retrying.","Unit-test all error-type branches of the handler."],"tags":["retrywhen","null-handler-result","observable","invalid-handler"],"backgroundTag":"invalid-argument-value","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"}