dotnet/reactive · error · ArgumentNullException
Value cannot be null. (Parameter 'scheduler')
Error message
Value cannot be null. (Parameter 'scheduler')
What it means
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.
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.
Example fix
// before var scheduled = source.SubscribeOn(_configuredScheduler); // null // after var scheduled = source.SubscribeOn(_configuredScheduler ?? Scheduler.Default);
Defensive patterns
Strategy: validation
Validate before calling
if (scheduler == null) scheduler = Scheduler.Default; var scheduled = source.SubscribeOn(scheduler);
Type guard
bool IsUsableScheduler(IScheduler scheduler) => scheduler is not null;
Try / catch
try
{
var scheduled = source.SubscribeOn(scheduler);
}
catch (ArgumentNullException ex) when (ex.ParamName == "scheduler")
{
var scheduled = source.SubscribeOn(Scheduler.Default);
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Value cannot be null. (Parameter 'action')
- Value cannot be null. (Parameter 'source')
- scheduler (Value cannot be null)
- action (Value cannot be null)
- scheduler (Value cannot be null)
AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15).
Data as JSON: /api/errors/cbf7de8163e27267.
Report an issue: GitHub.
Appendix: source
Thrown at Rx.NET/Source/src/System.Reactive/Concurrency/Synchronization.cs:40
/// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
/// <param name="source">Source sequence.</param>
/// <param name="scheduler">Scheduler to perform subscription and unsubscription actions on.</param>
/// <returns>The source sequence whose subscriptions and unsubscriptions happen on the specified scheduler.</returns>
/// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="scheduler"/> is <c>null</c>.</exception>
/// <remarks>
/// Only the side-effects of subscribing to the source sequence and disposing subscriptions to the source sequence are run on the specified scheduler.
/// 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)"/>.
/// </remarks>
public static IObservable<TSource> SubscribeOn<TSource>(IObservable<TSource> source, IScheduler scheduler)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
if (scheduler == null)
{
throw new ArgumentNullException(nameof(scheduler));
}
return new SubscribeOnObservable<TSource>(source, scheduler);
}
private sealed class SubscribeOnObservable<TSource> : ObservableBase<TSource>
{
private sealed class Subscription : IDisposable
{
private SerialDisposableValue _cancel;
public Subscription(IObservable<TSource> source, IScheduler scheduler, IObserver<TSource> observer)
{
_cancel.TrySetFirst(
scheduler.Schedule(
(@this: this, source, observer),
(closureScheduler, state) =>
{View on GitHub (pinned to 94b5d5ab91)