dotnet/reactive · error · ArgumentNullException

throw new ArgumentNullException(nameof(scheduler));

Error message

throw new ArgumentNullException(nameof(scheduler));

What it means

The Throw operator validates that both the error to emit and the scheduler used to schedule the emission are non-null. Passing a null IAsyncScheduler means the operator cannot schedule OnErrorAsync, so it fails fast with ArgumentNullException instead of failing later inside the scheduled work. This is standard eager argument validation performed synchronously when Throw is called.

Solutions

  1. Pass a concrete non-null IAsyncScheduler, e.g. ImmediateAsyncScheduler.Instance or a TestAsyncScheduler instance
  2. Check the code path producing the scheduler value and fix the null source (missing DI registration, failed lookup)
  3. If no scheduling is needed, use the Throw(error) overload that defaults to the immediate scheduler

Example fix

// before
var obs = AsyncObservable.Throw<int>(new Exception("boom"), scheduler); // scheduler is null
// after
var obs = AsyncObservable.Throw<int>(new Exception("boom"), scheduler ?? ImmediateAsyncScheduler.Instance);
Defensive patterns

Strategy: validation

Validate before calling

if (error == null) throw new ArgumentNullException(nameof(error));
if (scheduler == null) throw new ArgumentNullException(nameof(scheduler));
var obs = AsyncObservable.Throw<int>(error, scheduler);

Type guard

bool IsValidScheduler(IAsyncScheduler s) => s is not null;

Try / catch

try
{
    var obs = AsyncObservable.Throw<int>(error, scheduler);
}
catch (ArgumentNullException ex) when (ex.ParamName == "scheduler")
{
    // fall back to immediate scheduler
    var obs = AsyncObservable.Throw<int>(error, ImmediateAsyncScheduler.Instance);
}

Prevention

When it happens

Trigger: Calling AsyncObservable.Throw<TSource>(error, scheduler) with a null scheduler argument, e.g. a scheduler variable that was never initialized or a factory method that returned null.

Common situations: Developers resolving the scheduler from DI/configuration where registration is missing; passing the result of a lookup that returns null; refactoring code that previously used ImmediateAsyncScheduler.Instance.

Related errors


AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15). Data as JSON: /api/errors/1f19c67a3b2263a5. Report an issue: GitHub.

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/Throw.cs:25

namespace System.Reactive.Linq
{
    public partial class AsyncObservable
    {
        public static IAsyncObservable<TSource> Throw<TSource>(Exception error)
        {
            if (error == null)
                throw new ArgumentNullException(nameof(error));

            return Create<TSource>(observer => AsyncObserver.Throw(observer, error));
        }

        public static IAsyncObservable<TSource> Throw<TSource>(Exception error, IAsyncScheduler scheduler)
        {
            if (error == null)
                throw new ArgumentNullException(nameof(error));
            if (scheduler == null)
                throw new ArgumentNullException(nameof(scheduler));

            return Create<TSource>(observer => AsyncObserver.Throw(observer, error, scheduler));
        }
    }

    public partial class AsyncObserver
    {
        public static ValueTask<IAsyncDisposable> Throw<TSource>(IAsyncObserver<TSource> observer, Exception error) => Throw(observer, error, ImmediateAsyncScheduler.Instance);

        public static ValueTask<IAsyncDisposable> Throw<TSource>(IAsyncObserver<TSource> observer, Exception error, IAsyncScheduler scheduler)
        {
            if (observer == null)
                throw new ArgumentNullException(nameof(observer));
            if (scheduler == null)
                throw new ArgumentNullException(nameof(scheduler));

            return scheduler.ScheduleAsync(async ct =>
            {

View on GitHub (pinned to 94b5d5ab91)