{"record":{"id":"cbf7de8163e27267","repo":"dotnet/reactive","slug":"value-cannot-be-null-parameter-scheduler-synchronization","errorCode":null,"errorMessage":"Value cannot be null. (Parameter 'scheduler')","messagePattern":"Value cannot be null\\. \\(Parameter 'scheduler'\\)","errorType":"exception","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"Rx.NET/Source/src/System.Reactive/Concurrency/Synchronization.cs","lineNumber":40,"sourceCode":"        /// <typeparam name=\"TSource\">The type of the elements in the source sequence.</typeparam>\n        /// <param name=\"source\">Source sequence.</param>\n        /// <param name=\"scheduler\">Scheduler to perform subscription and unsubscription actions on.</param>\n        /// <returns>The source sequence whose subscriptions and unsubscriptions happen on the specified scheduler.</returns>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"source\"/> or <paramref name=\"scheduler\"/> is <c>null</c>.</exception>\n        /// <remarks>\n        /// Only the side-effects of subscribing to the source sequence and disposing subscriptions to the source sequence are run on the specified scheduler.\n        /// In order to invoke observer callbacks on the specified scheduler, e.g. to offload callback processing to a dedicated thread, use <see cref=\"Synchronization.ObserveOn{TSource}(IObservable{TSource}, IScheduler)\"/>.\n        /// </remarks>\n        public static IObservable<TSource> SubscribeOn<TSource>(IObservable<TSource> source, IScheduler scheduler)\n        {\n            if (source == null)\n            {\n                throw new ArgumentNullException(nameof(source));\n            }\n\n            if (scheduler == null)\n            {\n                throw new ArgumentNullException(nameof(scheduler));\n            }\n\n            return new SubscribeOnObservable<TSource>(source, scheduler);\n        }\n\n        private sealed class SubscribeOnObservable<TSource> : ObservableBase<TSource>\n        {\n            private sealed class Subscription : IDisposable\n            {\n                private SerialDisposableValue _cancel;\n\n                public Subscription(IObservable<TSource> source, IScheduler scheduler, IObserver<TSource> observer)\n                {\n                    _cancel.TrySetFirst(\n                        scheduler.Schedule(\n                            (@this: this, source, observer),\n                            (closureScheduler, state) =>\n                            {","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/Rx.NET/Source/src/System.Reactive/Concurrency/Synchronization.cs#L22-L58","documentation":"ArgumentNullException thrown by Synchronization.SubscribeOn<TSource>(IObservable<TSource>, IScheduler) when the scheduler argument is null. The scheduler is required to move the subscription onto a thread; without it the operation has no meaning, so the method validates it immediately after validating the source.","triggerScenarios":"Calling source.SubscribeOn(scheduler) where the IScheduler is null — e.g. an injected scheduler dependency that was not registered in DI, a scheduler factory returning null, or a previously implicit default-scheduler overload migrated to an explicit null argument.","commonSituations":"DI misconfiguration (IScheduler not registered); unit tests passing null schedulers; Rx version migrations where default scheduler overloads were removed and callers now pass null explicitly.","solutions":["Pass a concrete IScheduler such as Scheduler.ThreadPool, Scheduler.NewThread, Scheduler.Default, or a test scheduler like TestScheduler.","Fix DI or instance initialization so the scheduler dependency is provided before SubscribeOn is called.","Null-check the scheduler and fall back to Scheduler.Default or Scheduler.Immediate when none is configured.","Use the SynchronizationContext overload instead if a context is available and the scheduler is not.","Verify the scheduler-producing factory or config lookup cannot return null in your environment."],"exampleFix":"// before\nvar scheduled = source.SubscribeOn(_configuredScheduler); // null\n// after\nvar scheduled = source.SubscribeOn(_configuredScheduler ?? Scheduler.Default);","handlingStrategy":"validation","validationCode":"if (scheduler == null) scheduler = Scheduler.Default;\nvar scheduled = source.SubscribeOn(scheduler);","typeGuard":"bool IsUsableScheduler(IScheduler scheduler) => scheduler is not null;","tryCatchPattern":"try\n{\n    var scheduled = source.SubscribeOn(scheduler);\n}\ncatch (ArgumentNullException ex) when (ex.ParamName == \"scheduler\")\n{\n    var scheduled = source.SubscribeOn(Scheduler.Default);\n}","preventionTips":["Register IScheduler in DI and assert it resolves at application startup.","Never pass an explicitly-null scheduler; default to Scheduler.Default or Scheduler.Immediate.","In tests, use TestScheduler or Scheduler.Immediate instead of null.","When migrating Rx versions, replace removed default-scheduler overloads with an explicit valid scheduler."],"tags":["csharp","dotnet","null-argument","rx","scheduler"],"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"}