dotnet/reactive · error · InvalidOperationException
AdvanceTo cannot be called when the scheduler is already…
Error message
AdvanceTo cannot be called when the scheduler is already running. Try using Sleep instead.
What it means
AdvanceTo runs a dispatch loop and therefore requires the scheduler not to be running. If the scheduler is already enabled (IsEnabled == true, e.g. the call is made inside a scheduled action or from within a prior Start/AdvanceTo loop), a nested dispatch loop would occur, so an InvalidOperationException is thrown with this message.
Solutions
- Use scheduler.Sleep(relativeTime) to slip time while the scheduler is running, as the message suggests.
- Move the AdvanceTo call outside the scheduled work — schedule a new action or advance after the current loop finishes.
- Restructure test code so clock advancement happens only from the outer test thread when IsEnabled is false.
Example fix
// before scheduler.Schedule(() => scheduler.AdvanceTo(targetTime)); // inside running scheduler // after scheduler.Schedule(() => scheduler.Sleep(TimeSpan.FromMilliseconds(50)));
Defensive patterns
Strategy: validation
Validate before calling
if (scheduler.IsEnabled)
scheduler.Sleep(relativeSlip);
else
scheduler.AdvanceTo(time); Type guard
bool CanAdvance<TAbsolute, TRelative>(VirtualTimeSchedulerBase<TAbsolute, TRelative> s) => !s.IsEnabled;
Try / catch
try { scheduler.AdvanceTo(time); } catch (InvalidOperationException ex) when (ex.Message.Contains(nameof(VirtualTimeSchedulerBase<TAbsolute, TRelative>.AdvanceTo))) { scheduler.Sleep(remainingSlip); } Prevention
- Never call AdvanceTo/Start from inside code the scheduler itself invokes; use Sleep there.
- Keep clock advancement on the outer test thread and check IsEnabled first.
- For time slippage during running work, prefer Sleep(relativeTime).
When it happens
Trigger: Calling AdvanceTo from inside work executed by the same virtual scheduler (a scheduled action re-advancing time), or calling AdvanceTo/Start re-entrantly while a dispatch loop is active.
Common situations: Test actions that schedule more work and try to advance the clock from within the callback; event handlers triggered by scheduled work that call back into the test harness; Rx operators that schedule follow-up work and user code hooks into it.
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
- AdvanceBy cannot be called when the scheduler is already…
- The subject has no value.
- Specified argument was out of the range of valid values…
- Specified argument was out of the range of valid values…
- Specified argument was out of the range of valid values…
AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15).
Data as JSON: /api/errors/9a40af117163938f.
Report an issue: GitHub.
Appendix: source
Thrown at Rx.NET/Source/src/System.Reactive/Concurrency/VirtualTimeScheduler.cs:241
{
if (Comparer.Compare(next.DueTime, Clock) > 0)
{
Clock = next.DueTime;
}
next.Invoke();
}
else
{
IsEnabled = false;
}
} while (IsEnabled);
Clock = time;
}
else
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Strings_Linq.CANT_ADVANCE_WHILE_RUNNING, nameof(AdvanceTo)));
}
}
/// <summary>
/// Advances the scheduler's clock by the specified relative time, running all work scheduled for that timespan.
/// </summary>
/// <param name="time">Relative time to advance the scheduler's clock by.</param>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="time"/> is negative.</exception>
/// <exception cref="InvalidOperationException">The scheduler is already running. VirtualTimeScheduler doesn't support running nested work dispatch loops. To simulate time slippage while running work on the scheduler, use <see cref="Sleep"/>.</exception>
public void AdvanceBy(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)