{"record":{"id":"e923807f9a6ef7f7","repo":"dotnet/reactive","slug":"scheduler-value-cannot-be-null-services-emulation","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.Services.Emulation.cs","lineNumber":32,"sourceCode":"        /// <summary>\n        /// Schedules a periodic piece of work by dynamically discovering the scheduler's capabilities.\n        /// If the scheduler supports periodic scheduling, the request will be forwarded to the periodic scheduling implementation.\n        /// If the scheduler provides stopwatch functionality, the periodic task will be emulated using recursive scheduling with a stopwatch to correct for time slippage.\n        /// Otherwise, the periodic task will be emulated using recursive scheduling.\n        /// </summary>\n        /// <typeparam name=\"TState\">The type of the state passed to the scheduled action.</typeparam>\n        /// <param name=\"scheduler\">The scheduler to run periodic work on.</param>\n        /// <param name=\"state\">Initial state passed to the action upon the first iteration.</param>\n        /// <param name=\"period\">Period for running the work periodically.</param>\n        /// <param name=\"action\">Action to be executed, potentially updating the state.</param>\n        /// <returns>The disposable object used to cancel the scheduled recurring action (best effort).</returns>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"scheduler\"/> or <paramref name=\"action\"/> is <c>null</c>.</exception>\n        /// <exception cref=\"ArgumentOutOfRangeException\"><paramref name=\"period\"/> is less than <see cref=\"TimeSpan.Zero\"/>.</exception>\n        public static IDisposable SchedulePeriodic<TState>(this IScheduler scheduler, TState state, TimeSpan period, Func<TState, TState> action)\n        {\n            if (scheduler == null)\n            {\n                throw new ArgumentNullException(nameof(scheduler));\n            }\n\n            if (period < TimeSpan.Zero)\n            {\n                throw new ArgumentOutOfRangeException(nameof(period));\n            }\n\n            if (action == null)\n            {\n                throw new ArgumentNullException(nameof(action));\n            }\n\n            return SchedulePeriodic_(scheduler, state, period, action);\n        }\n\n        /// <summary>\n        /// Schedules a periodic piece of work by dynamically discovering the scheduler's capabilities.\n        /// If the scheduler supports periodic scheduling, the request will be forwarded to the periodic scheduling implementation.","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/Rx.NET/Source/src/System.Reactive/Concurrency/Scheduler.Services.Emulation.cs#L14-L50","documentation":"SchedulePeriodic<TState>(IScheduler, TState, TimeSpan, Func<TState,TState>) in Scheduler.Services.Emulation.cs throws ArgumentNullException when the IScheduler is null. The extension method needs a scheduler instance to run the periodic loop on; a null receiver cannot be dispatched. Rx validates this eagerly rather than failing when the periodic timer starts.","triggerScenarios":"Invoking scheduler.SchedulePeriodic(state, period, action) with a null scheduler reference, e.g. IScheduler s = null; s.SchedulePeriodic(state, TimeSpan.FromSeconds(1), x => x), or passing a scheduler factory/property that returned null.","commonSituations":"Injecting IScheduler via DI where no binding is registered (null default), a configuration-selected scheduler that fell back to null, calling through a wrapper class whose scheduler field was never initialized, or code moved off DefaultScheduler during a refactor.","solutions":["Pass a concrete scheduler such as Scheduler.Default, Scheduler.ThreadPool, or a test scheduler (TestScheduler) instead of null","If the scheduler is resolved from DI/config, ensure the binding exists and check for null before calling","Guard the call site: if (scheduler is null) throw new ArgumentNullException(nameof(scheduler)); with a clearer message"],"exampleFix":"// before\nIScheduler scheduler = config.GetScheduler(); // returns null\nscheduler.SchedulePeriodic(state, TimeSpan.FromSeconds(1), s => s);\n\n// after\nIScheduler scheduler = config.GetScheduler() ?? Scheduler.Default;\nscheduler.SchedulePeriodic(state, TimeSpan.FromSeconds(1), s => s);","handlingStrategy":"validation","validationCode":"if (scheduler is null) throw new ArgumentNullException(nameof(scheduler));\nif (period < TimeSpan.Zero) throw new ArgumentOutOfRangeException(nameof(period));\nif (action is null) throw new ArgumentNullException(nameof(action));\nscheduler.SchedulePeriodic(state, period, action);","typeGuard":"bool CanSchedulePeriodic(IScheduler s) => s is not null;","tryCatchPattern":"try { scheduler.SchedulePeriodic(state, period, action); }\ncatch (ArgumentNullException ex) when (ex.ParamName == \"scheduler\") { scheduler = Scheduler.Default; /* retry */ }","preventionTips":["Resolve IScheduler through DI with Scheduler.Default as fallback binding","Avoid nullable IScheduler fields; use Lazy<IScheduler> or static readonly","In tests always construct a TestScheduler instead of passing null stubs"],"tags":["rx","scheduler","argument-null"],"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"}