{"record":{"id":"771a4c8d46e0732f","repo":"dotnet/reactive","slug":"winrt-no-sub1ms-timers-parameter-period","errorCode":null,"errorMessage":"WINRT_NO_SUB1MS_TIMERS (Parameter 'period')","messagePattern":"WINRT_NO_SUB1MS_TIMERS \\(Parameter 'period'\\)","errorType":"validation","errorClass":"ArgumentOutOfRangeException","httpStatus":null,"severity":"error","filePath":"Rx.NET/Source/src/System.Reactive/Platforms/WinRT/Concurrency/ThreadPoolScheduler.Windows.cs","lineNumber":187,"sourceCode":"        /// Schedules a periodic piece of work, using a Windows.System.Threading.ThreadPoolTimer 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 null.</exception>\n        /// <exception cref=\"ArgumentOutOfRangeException\"><paramref name=\"period\"/> is less than one millisecond.</exception>\n        public IDisposable SchedulePeriodic<TState>(TState state, TimeSpan period, Func<TState, TState> action)\n        {\n            //\n            // The WinRT thread pool is based on the Win32 thread pool and cannot handle\n            // sub-1ms resolution. When passing a lower period, we get single-shot\n            // timer behavior instead. See MSDN documentation for CreatePeriodicTimer\n            // for more information.\n            //\n            if (period < TimeSpan.FromMilliseconds(1))\n                throw new ArgumentOutOfRangeException(nameof(period), Strings_PlatformServices.WINRT_NO_SUB1MS_TIMERS);\n            if (action == null)\n                throw new ArgumentNullException(nameof(action));\n\n            return new PeriodicallyScheduledWorkItem<TState>(state, period, action);\n        }\n\n        private sealed class PeriodicallyScheduledWorkItem<TState> : IDisposable\n        {\n            private TState _state;\n            private Func<TState, TState> _action;\n\n            private readonly ThreadPoolTimer _timer;\n            private readonly AsyncLock _gate = new();\n\n            public PeriodicallyScheduledWorkItem(TState state, TimeSpan period, Func<TState, TState> action)\n            {\n                _state = state;\n                _action = action;","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/Rx.NET/Source/src/System.Reactive/Platforms/WinRT/Concurrency/ThreadPoolScheduler.Windows.cs#L169-L205","documentation":"ThreadPoolScheduler.Windows.SchedulePeriodic<TState>(state, period, action) throws ArgumentOutOfRangeException with message 'WINRT_NO_SUB1MS_TIMERS' because 'period' is less than 1 millisecond. The WinRT thread pool is built on the Win32 thread pool and cannot do sub-1ms periodic timers — such values degrade into single-shot timers — so Rx rejects them with a descriptive message instead of silently misbehaving.","triggerScenarios":"Calling SchedulePeriodic with period values like TimeSpan.Zero, TimeSpan.FromTicks(...), TimeSpan.FromMilliseconds(0.5), or a computed sub-millisecond interval (e.g. TimeSpan.FromSeconds(0.0001)).","commonSituations":"Porting code written for desktop schedulers (which support ~1ms or sub-ms timers) to a UWP/WinRT app; math producing zero-length intervals; attempting high-frequency polling that WinRT cannot support.","solutions":["Use a period of at least TimeSpan.FromMilliseconds(1); if you need sub-ms periodic work, redesign (batch work, or use a busy loop on a dedicated thread).","Clamp: period = period < TimeSpan.FromMilliseconds(1) ? TimeSpan.FromMilliseconds(1) : period.","If a one-shot near-immediate execution is intended, call Schedule(state, TimeSpan.Zero, action) instead of SchedulePeriodic."],"exampleFix":"// before\nscheduler.SchedulePeriodic(0, TimeSpan.Zero, Tick); // throws WINRT_NO_SUB1MS_TIMERS\n// after\nvar period = TimeSpan.FromMilliseconds(1); // WinRT minimum\nscheduler.SchedulePeriodic(0, period, Tick);","handlingStrategy":"validation","validationCode":"// csharp\nconst TimeSpan MinWinrtPeriod = TimeSpan.FromMilliseconds(1);\nif (period < MinWinrtPeriod)\n    period = MinWinrtPeriod; // WinRT thread pool cannot do sub-1ms periodic timers\nscheduler.SchedulePeriodic(state, period, action);","typeGuard":"null","tryCatchPattern":"// csharp\ntry { d = scheduler.SchedulePeriodic(state, period, action); }\ncatch (ArgumentOutOfRangeException ex) when (ex.ParamName == \"period\") { log.Error($\"Period {period} below WinRT 1ms minimum\", ex); d = Disposable.Empty; }","preventionTips":["Always clamp periodic periods to at least 1ms when targeting WinRT/UWP","Do not assume desktop Rx timer resolution when porting code to WinRT","If sub-ms frequency is required, use a dedicated busy-wait thread instead of SchedulePeriodic"],"tags":["argument-out-of-range","scheduler","timer","platform-limitation","winrt"],"backgroundTag":"value-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"}