nautechsystems/nautilus_trader · error

timer exists at timestamp

Error message

timer exists at timestamp

What it means

BacktestEngine::run_timer_handlers_at pops due time events from the accumulator after peeking that the accumulator's next time equals ts_event. The expect asserts the pop always succeeds right after the successful peek. Failure would mean the accumulator was drained between the peek and the pop, or peek/pop disagree — an internal consistency bug in the timer loop.

Source

Thrown at crates/backtest/src/engine.rs:1698

                self.last_ns = t;
                self.flush_accumulator_events(clocks, t)?;
                Ok(false)
            }
        }
    }

    fn run_timer_handlers_at(
        &mut self,
        clocks: &[Rc<RefCell<dyn Clock>>],
        ts_event: UnixNanos,
        advance_to: UnixNanos,
    ) {
        self.last_ns = ts_event;
        while self.accumulator.peek_next_time() == Some(ts_event) {
            let handler = self
                .accumulator
                .pop_next_at_or_before(ts_event)
                .expect("timer exists at timestamp");
            Self::set_all_clocks_time(clocks, ts_event);
            logging_clock_set_static_time(ts_event.as_u64());
            handler.run();
            self.drain_command_queues();

            if self.kernel.is_shutdown_requested() {
                return;
            }

            for clock in clocks {
                Self::advance_clock_on_accumulator(&mut self.accumulator, clock, advance_to, false);
            }
        }
    }

    fn finalize_timestamp(
        &mut self,
        clocks: &[Rc<RefCell<dyn Clock>>],

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure no code calls accumulator methods concurrently with engine time advancement
  2. File a minimal reproduction as a bug report if hit
  3. Upgrade to a version where the peek/pop mismatch is fixed
Defensive patterns

Strategy: type-guard

Validate before calling

// Users cannot peek/pop the accumulator directly; ensure timers are only scheduled
// via the engine API and never cancelled externally mid-run:
assert!(clock.timer_count() >= 0, "clock state sanity");

Try / catch

// Panic not catchable in normal Rust; reproduce with a timer-only backtest and report.

Prevention

When it happens

Trigger: Practically unreachable through the public run() API; would require peek_next_time() and pop_next_at_or_before() to disagree, e.g. via a logic regression or external mutation of the accumulator during the loop (called from advance_time_impl, flush_accumulator_events, finalize_timestamp).

Common situations: Only after an engine internals change/regression or a custom subclass interfering with the accumulator. Not a user configuration error.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/d4e3e65823f9dc8f. Report an issue: GitHub.