{"record":{"id":"c4f9a08b1dce10d3","repo":"dotnet/reactive","slug":"action-value-cannot-be-null","errorCode":null,"errorMessage":"action (Value cannot be null)","messagePattern":"action \\(Value cannot be null\\)","errorType":"validation","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"Rx.NET/Source/src/System.Reactive/Concurrency/Scheduler.Async.cs","lineNumber":386,"sourceCode":"        /// Schedules work using an asynchronous method, allowing for cooperative scheduling in an imperative coding style.\n        /// </summary>\n        /// <typeparam name=\"TState\">The type of the state passed to the scheduled action.</typeparam>\n        /// <param name=\"scheduler\">Scheduler to schedule work on.</param>\n        /// <param name=\"state\">State to pass to the asynchronous method.</param>\n        /// <param name=\"dueTime\">Absolute time at which to execute the action.</param>\n        /// <param name=\"action\">Asynchronous method to run the work, using Yield and Sleep operations for cooperative scheduling and injection of cancellation points.</param>\n        /// <returns>Disposable object that allows to cancel outstanding work on cooperative cancellation points or through the cancellation token passed to the asynchronous method.</returns>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"scheduler\"/> or <paramref name=\"action\"/> is <c>null</c>.</exception>\n        public static IDisposable ScheduleAsync<TState>(this IScheduler scheduler, TState state, DateTimeOffset dueTime, Func<IScheduler, TState, CancellationToken, Task<IDisposable>> action)\n        {\n            if (scheduler == null)\n            {\n                throw new ArgumentNullException(nameof(scheduler));\n            }\n\n            if (action == null)\n            {\n                throw new ArgumentNullException(nameof(action));\n            }\n\n            return ScheduleAsync_(scheduler, state, dueTime, action);\n        }\n\n        /// <summary>\n        /// Schedules work using an asynchronous method, allowing for cooperative scheduling in an imperative coding style.\n        /// </summary>\n        /// <param name=\"scheduler\">Scheduler to schedule work on.</param>\n        /// <param name=\"dueTime\">Absolute time at which to execute the action.</param>\n        /// <param name=\"action\">Asynchronous method to run the work, using Yield and Sleep operations for cooperative scheduling and injection of cancellation points.</param>\n        /// <returns>Disposable object that allows to cancel outstanding work on cooperative cancellation points or through the cancellation token passed to the asynchronous method.</returns>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"scheduler\"/> or <paramref name=\"action\"/> is <c>null</c>.</exception>\n        public static IDisposable ScheduleAsync(this IScheduler scheduler, DateTimeOffset dueTime, Func<IScheduler, CancellationToken, Task> action)\n        {\n            if (scheduler == null)\n            {\n                throw new ArgumentNullException(nameof(scheduler));","sourceCodeStart":368,"sourceCodeEnd":404,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/Rx.NET/Source/src/System.Reactive/Concurrency/Scheduler.Async.cs#L368-L404","documentation":"System.Reactive's ScheduleAsync validates the action delegate before scheduling. Supplying a null Func<IScheduler, TState, CancellationToken, Task<IDisposable>> causes an ArgumentNullException with message \"Value cannot be null (Parameter 'action')\". The guard guarantees the scheduled invocation always has a callable delegate.","triggerScenarios":"Calling scheduler.ScheduleAsync<TState>(state, DateTimeOffset dueTime, Func<IScheduler, TState, CancellationToken, Task<IDisposable>> action) with action == null, e.g. scheduler.ScheduleAsync(state, DateTimeOffset.Now, (Func<IScheduler, int, CancellationToken, Task<IDisposable>>)null).","commonSituations":"The cancelable-work delegate (returning Task<IDisposable>) is built dynamically or from configuration and comes out null; overload-resolution confusion leads to passing null; refactoring removed the callback.","solutions":["Pass a real delegate returning Task<IDisposable>, e.g. async (s, st, ct) => { ...; return Disposable.Empty; }.","Check action for null before calling ScheduleAsync.","Fix whatever produces the delegate so it cannot return null."],"exampleFix":"// before\nscheduler.ScheduleAsync(state, dueTime, null); // throws\n// after\nscheduler.ScheduleAsync(state, dueTime, async (s, st, ct) =>\n{\n    await DoWorkAsync(st, ct);\n    return Disposable.Empty;\n});","handlingStrategy":"validation","validationCode":"if (action == null) throw new InvalidOperationException(\"ScheduleAsync requires a non-null cancelable action\");","typeGuard":"bool IsValidAction<TState>(Func<IScheduler, TState, CancellationToken, Task<IDisposable>> a) => a is not null;","tryCatchPattern":"try { disposable = scheduler.ScheduleAsync(state, dueTime, action); }\ncatch (ArgumentNullException ex) when (ex.ParamName == \"action\") { disposable = Disposable.Empty; /* or reschedule with a valid delegate */ }","preventionTips":["For cancelable scheduled work, always return a Task<IDisposable> from a real delegate, using Disposable.Empty if nothing to dispose.","Validate delegates returned from factories before passing them on.","Avoid casting null literals to Func types at call sites."],"tags":["rx","scheduler","null-argument","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-15T23:17:13.987Z"}