nautechsystems/nautilus_trader · error

Cannot increment time while clock is in realtime mode

Error message

Cannot increment time while clock is in realtime mode

What it means

increment_time advances the TestClock's internal time, which is only meaningful when the clock is static (test/backtest mode). If the clock is in realtime mode, mutating time manually would violate the clock's invariants, so the operation is rejected with this error. The check is intentionally non-atomic: mode switches must be completed before time operations.

Source

Thrown at crates/core/src/time.rs:281

    /// Increments the current static-mode time by `delta` and returns the updated value.
    ///
    /// Internally this uses [`AtomicU64::try_update`] with [`Ordering::AcqRel`] to ensure the increment is
    /// atomic and visible to readers using `Acquire` loads.
    ///
    /// # Errors
    ///
    /// Returns an error if the increment would overflow `u64::MAX` or if called
    /// while the clock is in real-time mode.
    ///
    /// # Thread Safety
    ///
    /// The mode check is not atomic with the subsequent update. If another thread calls
    /// `make_realtime()` between the check and update, the invariant can be violated.
    /// This is intentional: mode switching is a setup-time operation and should not
    /// occur concurrently with time operations. Callers must ensure mode switches are
    /// complete before resuming time operations.
    pub fn increment_time(&self, delta: DurationNanos) -> anyhow::Result<UnixNanos> {
        anyhow::ensure!(
            !self.realtime.load(Ordering::SeqCst),
            "Cannot increment time while clock is in realtime mode"
        );

        let previous =
            match self
                .timestamp_ns
                .try_update(Ordering::AcqRel, Ordering::Acquire, |current| {
                    current.checked_add(delta.as_u64())
                }) {
                Ok(prev) => prev,
                Err(_) => anyhow::bail!("Cannot increment time beyond u64::MAX"),
            };

        debug_assert!(
            !self.realtime.load(Ordering::SeqCst),
            "Invariant: clock must remain in static mode across `increment_time`"
        );

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Call the clock's make_static/set_static equivalent before increment_time.
  2. Use a dedicated TestClock instance for time control and never switch it to realtime in the same test.
  3. Restructure the code so realtime clocks never receive manual time increments.

Example fix

// before
clock.set_realtime();
clock.increment_time(delta)?; // errors
// after
clock.make_static();
clock.increment_time(delta)?;
Defensive patterns

Strategy: type-guard

Validate before calling

// guard before mutating
if clock.is_realtime() { return Err(anyhow::anyhow!("clock is realtime; cannot increment")); }

Type guard

fn is_static(clock: &TestClock) -> bool { !clock.realtime.load(Ordering::SeqCst) } // conceptually; use the public accessor

Try / catch

clock.make_static();
clock.increment_time(delta)
    .context("failed to advance test clock")?;

Prevention

When it happens

Trigger: Calling clock.increment_time(delta) after clock.set_realtime(...) / make_realtime() without switching back to static mode. Typical in tests or live-node code that reuses a TestClock instance that was switched to realtime.

Common situations: Unit tests that flip a clock to realtime mid-test then try to fast-forward; backtest engines accidentally running with a realtime-mode clock; shared clock objects whose mode was changed elsewhere.

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 nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/da1ece7acebbd5cc. Report an issue: GitHub.