dotnet/reactive · error · ArgumentNullException

disposeScheduler

Error message

disposeScheduler

What it means

SubscribeOn(source, subscribeScheduler, disposeScheduler) validates the dispose scheduler last. A null disposeScheduler throws ArgumentNullException("disposeScheduler") because resource disposition must be scheduled onto it.

Solutions

  1. Use the single-scheduler overload SubscribeOn(source, scheduler) if both phases should use one scheduler.
  2. Pass a valid dispose scheduler instance explicitly.
  3. Fix whatever returns/assigns null to the dispose scheduler.

Example fix

// before
var obs = source.SubscribeOn(subscribeScheduler, null);
// after
var obs = source.SubscribeOn(subscribeScheduler); // same scheduler for subscribe and dispose
Defensive patterns

Strategy: validation

Validate before calling

if (disposeScheduler == null)
    disposeScheduler = subscribeScheduler; // or AsyncScheduler.DefaultScheduler

Type guard

bool HasDisposeScheduler(IAsyncScheduler? d) => d is not null;

Try / catch

try
{
    var obs = source.SubscribeOn(subscribeScheduler, disposeScheduler);
}
catch (ArgumentNullException ex) when (ex.ParamName == "disposeScheduler")
{
    logger.LogError(ex, "Null dispose scheduler");
    obs = source.SubscribeOn(subscribeScheduler);
}

Prevention

When it happens

Trigger: Calling source.SubscribeOn(subscribeScheduler, null) — often when the caller believes disposal reuses the subscribe scheduler, or the dispose scheduler variable was never assigned.

Common situations: Simplifying a call by dropping an argument; config lacking a separate dispose scheduler; refactoring from the single-scheduler overload leaving the second parameter null.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/SubscribeOn.cs:29

    {
        public static IAsyncObservable<TSource> SubscribeOn<TSource>(this IAsyncObservable<TSource> source, IAsyncScheduler scheduler)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (scheduler == null)
                throw new ArgumentNullException(nameof(scheduler));

            return SubscribeOn(source, scheduler, scheduler);
        }

        public static IAsyncObservable<TSource> SubscribeOn<TSource>(this IAsyncObservable<TSource> source, IAsyncScheduler subscribeScheduler, IAsyncScheduler disposeScheduler)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (subscribeScheduler == null)
                throw new ArgumentNullException(nameof(subscribeScheduler));
            if (disposeScheduler == null)
                throw new ArgumentNullException(nameof(disposeScheduler));

            return CreateAsyncObservable<TSource>.From(
                source,
                (subscribeScheduler, disposeScheduler),
                static async (source, state, observer) =>
                {
                    var m = new SingleAssignmentAsyncDisposable();
                    var d = new SerialAsyncDisposable();

                    await d.AssignAsync(m).ConfigureAwait(false);

                    var scheduled = await state.subscribeScheduler.ScheduleAsync(async ct =>
                    {
                        var subscription = await source.SubscribeSafeAsync(observer).RendezVous(state.subscribeScheduler, ct);

                        var scheduledDispose = AsyncDisposable.Create(async () =>
                        {
                            await state.disposeScheduler.ScheduleAsync(async _ =>

View on GitHub (pinned to 94b5d5ab91)