{"record":{"id":"b61744a6e7ba28cd","repo":"dotnet/reactive","slug":"period-dispatcherscheduler","errorCode":null,"errorMessage":"period","messagePattern":"period","errorType":"exception","errorClass":"ArgumentOutOfRangeException","httpStatus":null,"severity":"error","filePath":"Rx.NET/Source/src/System.Reactive/Platforms/Desktop/Concurrency/DispatcherScheduler.cs","lineNumber":181,"sourceCode":"\n            return d;\n        }\n\n        /// <summary>\n        /// 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();","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/Rx.NET/Source/src/System.Reactive/Platforms/Desktop/Concurrency/DispatcherScheduler.cs#L163-L199","documentation":"DispatcherScheduler.SchedulePeriodic validates that the period is not negative before creating the WPF DispatcherTimer. A negative period is meaningless for a timer and is rejected with ArgumentOutOfRangeException. Zero and positive periods are allowed.","triggerScenarios":"Calling scheduler.SchedulePeriodic(state, TimeSpan.FromMilliseconds(-1), action) or computing the period from a subtraction that went negative, e.g. TimeSpan.FromTicks(endTicks - startTicks) where start > end.","commonSituations":"Config values read as negative intervals; misordered subtraction of DateTimes; default/unset interval values like -1 standing in for 'not configured'.","solutions":["Pass a TimeSpan greater than or equal to TimeSpan.Zero as the period.","Clamp with a check before calling: if (period < TimeSpan.Zero) period = TimeSpan.Zero;","Fix the computation that produced the negative interval (e.g. swap operands of the subtraction)."],"exampleFix":"// before\nscheduler.SchedulePeriodic(state, TimeSpan.FromSeconds(interval), action); // interval = -5\n// after\nvar period = TimeSpan.FromSeconds(Math.Max(0, interval));\nscheduler.SchedulePeriodic(state, period, action);","handlingStrategy":"validation","validationCode":"if (period < TimeSpan.Zero)\n    throw new ArgumentOutOfRangeException(nameof(period), period, \"Period must be non-negative\");\nvar d = dispatcherScheduler.SchedulePeriodic(state, period, action);","typeGuard":"static bool IsValidPeriod(TimeSpan period) => period >= TimeSpan.Zero;","tryCatchPattern":"try { var d = scheduler.SchedulePeriodic(state, period, action); }\ncatch (ArgumentOutOfRangeException ex) when (ex.ParamName == \"period\") { log.Error($\"Bad period {ex.ActualValue}\"); }","preventionTips":["Clamp intervals read from configuration with Math.Max(TimeSpan.Zero, value)","Watch out for DateTime subtractions where operands can be reversed","Treat sentinel values like -1ms as 'not configured' and skip scheduling"],"tags":["argument-out-of-range","scheduler","timer","wpf"],"backgroundTag":"argument-out-of-range","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"}