dotnet/reactive · error · ArgumentOutOfRangeException

Specified argument was out of the range of valid values…

Error message

Specified argument was out of the range of valid values. (Parameter 'disposed')

What it means

TestScheduler.Start throws ArgumentOutOfRangeException for the 'disposed' parameter when the virtual disposal time is earlier than the creation time or earlier than the subscription time. The library requires created <= subscribed <= disposed so the test's virtual-time window is coherent; it fails fast to prevent tests that dispose an observable before it was subscribed.

Solutions

  1. Set disposed to a value >= subscribed (and >= created), e.g. ReactiveTest.Disposed (1000) or larger
  2. If using Start(create, disposed), pass disposed >= ReactiveTest.Subscribed (300)
  3. Extend the virtual time window if subscriptions legitimately need to run longer

Example fix

// before
var res = scheduler.Start(() => xs, 100, 200, 150);
// after
var res = scheduler.Start(() => xs, 100, 200, 1000); // disposed >= subscribed
Defensive patterns

Strategy: validation

Validate before calling

// C#
long disposed = ReactiveTest.Disposed; // 1000
if (disposed < ReactiveTest.Subscribed)
    throw new ArgumentException("disposed must be >= ReactiveTest.Subscribed (300)");
var res = scheduler.Start(() => xs, ReactiveTest.Created, ReactiveTest.Subscribed, disposed);

Type guard

static bool IsValidWindow(long created, long subscribed, long disposed) =>
    created <= subscribed && subscribed <= disposed;

Try / catch

try { var res = scheduler.Start(() => xs, disposed: myDisposed); }
catch (ArgumentOutOfRangeException ex) when (ex.ParamName == "disposed")
{
    // use ReactiveTest.Disposed or a larger tick
}

Prevention

When it happens

Trigger: Calling Start(create, created, subscribed, disposed) with disposed < created or disposed < subscribed, e.g. scheduler.Start(() => xs, 100, 200, 150). Also occurs with the Start(create, disposed) overload when a disposed value less than ReactiveTest.Subscribed (300) is passed, e.g. Start(() => xs, 100).

Common situations: Authoring marble tests with custom time windows where the disposal tick is set below the subscription tick; using Start(create, disposed) and forgetting the default disposed value must be >= 300 (ReactiveTest.Subscribed); test refactors that shrink the time window.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/Microsoft.Reactive.Testing/TestScheduler.cs:97

        /// <param name="disposed">Virtual time at which to dispose the subscription.</param>
        /// <returns>Observer with timestamped recordings of notification messages that were received during the virtual time window when the subscription to the source sequence was active.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="create"/> is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="subscribed"/> is less than <paramref name="created"/>.</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="disposed"/> is less than <paramref name="created"/> or <paramref name="subscribed"/>.</exception>
        public ITestableObserver<T> Start<T>(Func<IObservable<T>> create, long created, long subscribed, long disposed)
        {
            if (create == null)
            {
                throw new ArgumentNullException(nameof(create));
            }

            if (subscribed < created)
            {
                throw new ArgumentOutOfRangeException(nameof(subscribed));
            }
            if (disposed < created || disposed < subscribed)
            {
                throw new ArgumentOutOfRangeException(nameof(disposed));
            }

            var source = default(IObservable<T>);
            var subscription = default(IDisposable);
            var observer = CreateObserver<T>();

            ScheduleAbsolute(default(object), created, (scheduler, state) => { source = create(); return Disposable.Empty; });
            ScheduleAbsolute(default(object), subscribed, (scheduler, state) => { subscription = source.Subscribe(observer); return Disposable.Empty; });
            ScheduleAbsolute(default(object), disposed, (scheduler, state) => { subscription.Dispose(); return Disposable.Empty; });

            Start();

            return observer;
        }

        /// <summary>
        /// Starts the test scheduler and uses the specified virtual time to dispose the subscription to the sequence obtained through the factory function.
        /// Default virtual times are used for <see cref="ReactiveTest.Created">factory invocation</see> and <see cref="ReactiveTest.Subscribed">sequence subscription</see>.

View on GitHub (pinned to 94b5d5ab91)