{"record":{"id":"ae371b6e2b3e6de9","repo":"dotnet/reactive","slug":"action-parameter-action-threadpoolscheduler-windows","errorCode":null,"errorMessage":"action (Parameter 'action')","messagePattern":"action \\(Parameter 'action'\\)","errorType":"validation","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"Rx.NET/Source/src/System.Reactive/Platforms/WinRT/Concurrency/ThreadPoolScheduler.Windows.cs","lineNumber":95,"sourceCode":"\n        /// <summary>\n        /// Gets the options that configure how work is scheduled.\n        /// </summary>\n        [Obsolete(\"If you require the UWP-specific features of ThreadPoolScheduler use the WindowsRuntimeThreadPoolScheduler in the System.Reactive.WindowsRuntime package. This property will be removed in a future version (because UWP applications will end up with the same ThreadPoolScheduler as all other application types).\")]\n        public WorkItemOptions Options { get; }\n\n        /// <summary>\n        /// Schedules an action to be executed.\n        /// </summary>\n        /// <typeparam name=\"TState\">The type of the state passed to the scheduled action.</typeparam>\n        /// <param name=\"state\">State passed to the action to be executed.</param>\n        /// <param name=\"action\">Action to be executed.</param>\n        /// <returns>The disposable object used to cancel the scheduled action (best effort).</returns>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"action\"/> is null.</exception>\n        public override IDisposable Schedule<TState>(TState state, Func<IScheduler, TState, IDisposable> action)\n        {\n            if (action == null)\n                throw new ArgumentNullException(nameof(action));\n\n            var userWorkItem = new UserWorkItem<TState>(this, state, action);\n\n#pragma warning disable CS0618 // Type or member is obsolete.\n            // A note on obsolescence:\n            //  The compiler complains because this uses Priority and Options. We could mark the\n            //  whole method as obsolete, but this would be slightly misleading because when we\n            // eventually remove the obsoleted UWP support, this whole ThreadPoolScheduler will\n            // be replaced by the non-UWP implementation, and that continues to support this\n            // Schedule overload. So the method isn't really obsolete - it will continue to be\n            // available to UWP apps even after we've removed all UWP-specific code from\n            // System.Reactive.\n            // An argument in favour of marking the method as Obsolete anyway is that the\n            // behaviour will change once we remove UWP code from System.Reactive. However,\n            // the change in behaviour is interesting only if you've specified either\n            // priority or options for the work items, and all the public methods we supply\n            // for that *are* obsolete. So anyone relying on that behaviour will already have\n            // received an obsolescence warning, and should move to WindowsRuntimeThreadPoolScheduler.","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/Rx.NET/Source/src/System.Reactive/Platforms/WinRT/Concurrency/ThreadPoolScheduler.Windows.cs#L77-L113","documentation":"ThreadPoolScheduler.Windows.Schedule<TState>(state, action) throws ArgumentNullException because the 'action' to execute on the Windows thread pool is null. The scheduler wraps the action in a UserWorkItem for the WinRT thread pool, so a null delegate is rejected immediately.","triggerScenarios":"Calling ThreadPoolScheduler.Instance.Schedule(state, (scheduler, state) => ...) with a null Func<IScheduler,TState,IDisposable> — typically a null delegate variable or method returning null.","commonSituations":"Uninitialized fields holding work items; dependency-injected delegates not wired up; refactoring a lambda into a method whose return became null.","solutions":["Pass a non-null action delegate; initialize the variable holding it.","Throw a descriptive exception in your own code when the delegate is missing instead of letting the scheduler fail.","Skip scheduling entirely when there is no work to run."],"exampleFix":"// before\nscheduler.Schedule(state, (Func<IScheduler, int, IDisposable>)null);\n// after\nscheduler.Schedule(state, (s, st) => { RunWork(st); return Disposable.Empty; });","handlingStrategy":"validation","validationCode":"// csharp\nif (action == null)\n    return Disposable.Empty; // nothing to schedule\nscheduler.Schedule(state, action);","typeGuard":"// csharp\nbool CanSchedule<TState>(Func<IScheduler, TState, IDisposable> action) => action is not null;","tryCatchPattern":"// csharp\ntry { d = scheduler.Schedule(state, action); }\ncatch (ArgumentNullException ex) when (ex.ParamName == \"action\") { log.Warn(\"Work item was null\"); d = Disposable.Empty; }","preventionTips":["Prefer inline lambdas over delegate variables that can be null","Assert non-null delegates in debug builds before scheduling","Keep scheduling code away from conditional callback creation"],"tags":["null-argument","scheduler","thread-pool","csharp"],"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"}