{"record":{"id":"ec6fc6cae8861d9a","repo":"dotnet/reactive","slug":"scheduler-value-cannot-be-null-recursive","errorCode":null,"errorMessage":"scheduler (Value cannot be null)","messagePattern":"scheduler \\(Value cannot be null\\)","errorType":"validation","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"Rx.NET/Source/src/System.Reactive/Concurrency/Scheduler.Recursive.cs","lineNumber":22,"sourceCode":"\nusing System.Reactive.Disposables;\n\nnamespace System.Reactive.Concurrency\n{\n    public static partial class Scheduler\n    {\n        /// <summary>\n        /// Schedules an action to be executed recursively.\n        /// </summary>\n        /// <param name=\"scheduler\">Scheduler to execute the recursive action on.</param>\n        /// <param name=\"action\">Action to execute recursively. The parameter passed to the action is used to trigger recursive scheduling of the action.</param>\n        /// <returns>The disposable object used to cancel the scheduled action (best effort).</returns>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"scheduler\"/> or <paramref name=\"action\"/> is <c>null</c>.</exception>\n        public static IDisposable Schedule(this IScheduler scheduler, Action<Action> action)\n        {\n            if (scheduler == null)\n            {\n                throw new ArgumentNullException(nameof(scheduler));\n            }\n\n            if (action == null)\n            {\n                throw new ArgumentNullException(nameof(action));\n            }\n\n            return scheduler.Schedule(action, static (a, self) => a(() => self(a)));\n        }\n\n        /// <summary>\n        /// Schedules an action to be executed recursively.\n        /// </summary>\n        /// <typeparam name=\"TState\">The type of the state passed to the scheduled action.</typeparam>\n        /// <param name=\"scheduler\">Scheduler to execute the recursive action on.</param>\n        /// <param name=\"state\">State passed to the action to be executed.</param>\n        /// <param name=\"action\">Action to execute recursively. The last parameter passed to the action is used to trigger recursive scheduling of the action, passing in recursive invocation state.</param>\n        /// <returns>The disposable object used to cancel the scheduled action (best effort).</returns>","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/Rx.NET/Source/src/System.Reactive/Concurrency/Scheduler.Recursive.cs#L4-L40","documentation":"System.Reactive's recursive Schedule extension (Scheduler.Recursive.cs) requires a non-null IScheduler receiver and throws ArgumentNullException (Parameter 'scheduler') when it is null. The guard runs before the recursive-scheduling wrapper is constructed so invalid calls fail immediately.","triggerScenarios":"Calling scheduler.Schedule(Action<Action> action) on a null IScheduler receiver — permitted by the compiler because it is an extension method, e.g. `IScheduler s = null; s.Schedule(self => { ... });`.","commonSituations":"A scheduler dependency from DI/config is null; a scheduler returned by a provider method is null; refactoring removed the scheduler assignment while keeping the extension call.","solutions":["Invoke Schedule on a valid IScheduler instance (Scheduler.Default, TaskPoolScheduler.Default, TestScheduler).","Add `if (scheduler == null) scheduler = Scheduler.Default;` or use the null-coalescing operator before the call.","Repair whatever supplies the scheduler (DI registration, factory, property) so it is never null."],"exampleFix":"// before\nIScheduler scheduler = _options.Scheduler; // may be null\nscheduler.Schedule(self => DoWork(self)); // throws\n// after\nvar scheduler = _options.Scheduler ?? Scheduler.Default;\nscheduler.Schedule(self => DoWork(self));","handlingStrategy":"validation","validationCode":"if (scheduler == null) scheduler = Scheduler.Default;","typeGuard":"bool HasScheduler(IScheduler s) => s is not null;","tryCatchPattern":"try { disposable = scheduler.Schedule(self => next => { /* recursive work */ }); }\ncatch (ArgumentNullException ex) when (ex.ParamName == \"scheduler\") { disposable = Scheduler.Default.Schedule(self => next => { /* recursive work */ }); }","preventionTips":["Always obtain schedulers from Rx statics (Scheduler.Default, Scheduler.Immediate) or validated injection.","Validate scheduler arguments in your own wrapper methods before delegating to Rx.","Never hold IScheduler in nullable fields that can be read before initialization."],"tags":["rx","scheduler","null-argument","csharp"],"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"}