dotnet/reactive · error · ArgumentOutOfRangeException

Specified argument was out of the range of valid values. (Pa

Error message

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

What it means

TestScheduler.Start throws ArgumentOutOfRangeException for the 'subscribed' parameter when the virtual time at which the subscription happens is earlier than the virtual time at which the observable is created. The test scheduler enforces monotonic virtual timestamps: created <= subscribed <= disposed. It throws so tests with nonsensical time windows fail fast instead of producing misleading results.

Solutions

  1. Reorder the timestamps so subscribed >= created (and disposed >= subscribed); typically use ReactiveTest.Created and ReactiveTest.Subscribed defaults
  2. If using Start(create, subscribed, disposed) with custom values, ensure the subscribed value is >= ReactiveTest.Created (200)
  3. Swap arguments if created/subscribed were transposed by mistake

Example fix

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

Strategy: validation

Validate before calling

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

Type guard

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

Try / catch

try { var res = scheduler.Start(() => xs, created, subscribed, disposed); }
catch (ArgumentOutOfRangeException ex) when (ex.ParamName == "subscribed")
{
    // fix timestamps: ensure subscribed >= created
}

Prevention

When it happens

Trigger: Calling any Start overload (e.g. scheduler.Start(() => xs, created, subscribed, disposed)) with a 'subscribed' value less than the 'created' value, e.g. Start(() => xs, 100, 50, 200) or Start(() => xs, subscribed: 0, ...) after a non-zero created time. Also hit indirectly by the 3-argument Start overload when ReactiveTest.Subscribed (default 200) is less than a custom created value passed in.

Common situations: Hand-written marble tests where the author mixes up the ordering of Created (200), Subscribed (300), Disposed (1000) defaults and passes custom values in the wrong order; refactoring tests with custom virtual timestamps; copying a Start call and editing timestamps so subscribed < created.

Related errors


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

Appendix: source

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

        /// <typeparam name="T">The element type of the observable sequence being tested.</typeparam>
        /// <param name="create">Factory method to create an observable sequence.</param>
        /// <param name="created">Virtual time at which to invoke the factory to create an observable sequence.</param>
        /// <param name="subscribed">Virtual time at which to subscribe to the created observable sequence.</param>
        /// <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;
        }

View on GitHub (pinned to 94b5d5ab91)