dotnet/reactive · error · InvalidOperationException

AdvanceBy cannot be called when the scheduler is already run

Error message

AdvanceBy cannot be called when the scheduler is already running. Try using Sleep instead.

What it means

VirtualTimeScheduler's AdvanceBy (and AdvanceTo) can only be called while the scheduler clock is not running. When the scheduler is enabled (e.g. inside a test that has started the scheduler or during Start/Stop), advancing the clock directly would corrupt the internal ordering, so System.Reactive throws InvalidOperationException and points you at Sleep as the valid alternative inside a running scheduler.

Solutions

  1. Move the AdvanceBy call outside the running scheduler (after Start()/Stop() completes, or when IsEnabled is false)
  2. If you need a delay inside running scheduled work, schedule a relative item via scheduler.Schedule(dueTime, action) instead of advancing the clock
  3. Catch InvalidOperationException only if the running state is expected, and fall back to Sleep semantics or defer the advance

Example fix

// before
scheduler.Schedule(TimeSpan.FromTicks(10), () =>
{
    scheduler.AdvanceBy(TimeSpan.FromTicks(5)); // throws when running
});
// after
scheduler.Schedule(TimeSpan.FromTicks(10), () =>
{
    // do work; use relative scheduling for delays instead
    scheduler.Schedule(TimeSpan.FromTicks(5), () => { /* later work */ });
});
Defensive patterns

Strategy: validation

Validate before calling

if (scheduler.IsEnabled) throw new InvalidOperationException("Use Sleep/relative scheduling instead of AdvanceBy while running");
scheduler.AdvanceBy(ts);

Type guard

bool CanAdvance(VirtualTimeSchedulerBase<long, long> s) => !s.IsEnabled;

Prevention

When it happens

Trigger: Calling scheduler.AdvanceBy(ts) or scheduler.AdvanceTo(t) when IsEnabled is true — typically advancing the clock from inside a scheduled action, or after Start()/Stop() left the scheduler running.

Common situations: Rx TestScheduler-based unit tests where a callback scheduled on the virtual clock tries to advance time; nested test steps that re-enter the scheduler while it is executing queued work.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Concurrency/VirtualTimeScheduler.cs:272

            var dueToClock = Comparer.Compare(dt, Clock);
            if (dueToClock < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(time));
            }

            if (dueToClock == 0)
            {
                return;
            }

            if (!IsEnabled)
            {
                AdvanceTo(dt);
            }
            else
            {
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Strings_Linq.CANT_ADVANCE_WHILE_RUNNING, nameof(AdvanceBy)));
            }
        }

        /// <summary>
        /// Advances the scheduler's clock by the specified relative time.
        /// </summary>
        /// <param name="time">Relative time to advance the scheduler's clock by.</param>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="time"/> is negative.</exception>
        public void Sleep(TRelative time)
        {
            var dt = Add(Clock, time);

            var dueToClock = Comparer.Compare(dt, Clock);
            if (dueToClock < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(time));
            }

View on GitHub (pinned to 94b5d5ab91)