{"record":{"id":"10d99a909ba2efb2","repo":"dotnet/reactive","slug":"argumentnullexception-action","errorCode":null,"errorMessage":"ArgumentNullException: action","messagePattern":"ArgumentNullException: action","errorType":"validation","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"Rx.NET/Source/src/System.Reactive.WindowsRuntime/System.Reactive.Concurrency/CoreDispatcherScheduler.cs","lineNumber":249,"sourceCode":"        /// <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            //\n            // According to MSDN documentation, the default is TimeSpan.Zero, so that's definitely valid.\n            // Empirical observation - negative values seem to be normalized to TimeSpan.Zero, but let's not go there.\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 = CreateDispatcherQueue().CreateTimer();\n\n            var state1 = state;\n\n            timer.Tick += (o, 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":231,"sourceCodeEnd":267,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/Rx.NET/Source/src/System.Reactive.WindowsRuntime/System.Reactive.Concurrency/CoreDispatcherScheduler.cs#L231-L267","documentation":"After validating the period, SchedulePeriodic validates the action delegate and throws ArgumentNullException (named action) when it is null. The periodic timer created from the DispatcherQueue would have nothing to invoke, so null is rejected up front.","triggerScenarios":"Calling scheduler.SchedulePeriodic(state, TimeSpan.FromMinutes(1), null) or a wrapper forwarding a null periodic callback.","commonSituations":"Periodic poll/heartbeat helpers with optional callbacks; delegates lost during refactoring; test scaffolding passing null placeholders.","solutions":["Supply a concrete periodic action; use a no-op body returning the state if nothing needs doing.","Null-check the callback at the call site before scheduling.","Fix upstream assignment so the periodic callback is initialized."],"exampleFix":"// before\nscheduler.SchedulePeriodic(state, TimeSpan.FromSeconds(30), null);\n// after\nscheduler.SchedulePeriodic(state, TimeSpan.FromSeconds(30), st => { Heartbeat(st); return st; });","handlingStrategy":"validation","validationCode":"if (action == null) throw new ArgumentException(\"action required\");\nscheduler.SchedulePeriodic(state, period, action);","typeGuard":"bool Schedulable<TState>(Func<TState, TState>? a) => a is not null;","tryCatchPattern":"try { scheduler.SchedulePeriodic(state, period, action); }\ncatch (ArgumentNullException ex) when (ex.ParamName == \"action\") { /* fix caller */ }","preventionTips":["Supply real periodic work or skip scheduling entirely","Initialize periodic callbacks before constructing timers","Avoid nullable delegate fields feeding SchedulePeriodic"],"tags":["null-argument","csharp","scheduler","timer"],"backgroundTag":"null-argument","analyzedSha":"94b5d5ab912789f5abe9a72138a25bbd716fe59c","analyzedAt":"2026-09-15T02:26:24.759Z","contentChangedAt":"2026-09-15T02:26:24.759Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}