{"record":{"id":"309673d23cda01ad","repo":"dotnet/reactive","slug":"action-dispatcherscheduler","errorCode":null,"errorMessage":"action","messagePattern":"action","errorType":"exception","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"Rx.NET/Source/src/System.Reactive/Platforms/Desktop/Concurrency/DispatcherScheduler.cs","lineNumber":186,"sourceCode":"        /// Schedules a periodic piece of work on the dispatcher, using a <see cref=\"System.Windows.Threading.DispatcherTimer\"/> object.\n        /// </summary>\n        /// <typeparam name=\"TState\">The type of the state passed to the scheduled action.</typeparam>\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=\"action\"/> is <c>null</c>.</exception>\n        /// <exception cref=\"ArgumentOutOfRangeException\"><paramref name=\"period\"/> is less than <see cref=\"TimeSpan.Zero\"/>.</exception>\n        public IDisposable SchedulePeriodic<TState>(TState state, TimeSpan period, Func<TState, TState> action)\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            var timer = new System.Windows.Threading.DispatcherTimer(Priority, Dispatcher);\n\n            var state1 = state;\n\n            timer.Tick += (s, e) =>\n            {\n                state1 = action(state1);\n            };\n\n            timer.Interval = period;\n            timer.Start();\n\n            return Disposable.Create(() =>\n            {\n                var t = Interlocked.Exchange(ref timer, null);\n                if (t != null)","sourceCodeStart":168,"sourceCodeEnd":204,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/Rx.NET/Source/src/System.Reactive/Platforms/Desktop/Concurrency/DispatcherScheduler.cs#L168-L204","documentation":"After validating the period, SchedulePeriodic checks the action delegate for null because the periodic DispatcherTimer tick will invoke it on every interval. A null action would crash inside the timer callback, so it throws ArgumentNullException up front.","triggerScenarios":"Calling scheduler.SchedulePeriodic(state, TimeSpan.FromSeconds(1), null) or passing a Func<TState, TState> field/variable that was never assigned.","commonSituations":"Periodic-poll wrappers whose callback is supplied conditionally; delegates captured from event handlers that have not fired yet; deserialized configuration objects with null function members.","solutions":["Pass a non-null Func<TState, TState> to SchedulePeriodic.","Initialize the delegate before starting the periodic schedule.","Return an identity function (st => st) if the state does not need updating each tick."],"exampleFix":"// before\nscheduler.SchedulePeriodic(state, period, periodicAction); // periodicAction is null\n// after\nperiodicAction ??= st => st;\nvar d = scheduler.SchedulePeriodic(state, period, periodicAction);","handlingStrategy":"validation","validationCode":"if (action == null) throw new InvalidOperationException(\"Periodic schedule requires a non-null action\");\nif (period < TimeSpan.Zero) throw new ArgumentOutOfRangeException(nameof(period));\nvar d = dispatcherScheduler.SchedulePeriodic(state, period, action);","typeGuard":"static bool IsValidPeriodic<TState>(TimeSpan period, Func<TState, TState> action) => period >= TimeSpan.Zero && action is not null;","tryCatchPattern":"try { var d = scheduler.SchedulePeriodic(state, period, action); }\ncatch (ArgumentNullException ex) when (ex.ParamName == \"action\") { log.Error(\"Null periodic action\"); }","preventionTips":["Default the periodic action to an identity function when optional","Validate all timer parameters in a single guard method","Avoid deserializing delegates from config; construct them in code"],"tags":["argument-null","scheduler","timer","wpf"],"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"}