nautechsystems/nautilus_trader · error
timer queue peeked Some but pop returned None
Error message
timer queue peeked Some but pop returned None
What it means
In LiveClock/advance_time, the timer queue is peeked and, if an entry is due, immediately popped. The expect fires if the BinaryHeap yields None on pop right after a successful peek — an internal invariant violation that should be impossible in single-threaded code (duplicate peek/pop race or corrupted queue).
Source
Thrown at crates/common/src/clock.rs:959
to_time_ns >= from_time_ns,
"Invariant: time must be non-decreasing, `to_time_ns` {to_time_ns} < `from_time_ns` {from_time_ns}"
);
if set_time {
self.time.set_time(to_time_ns);
}
let mut events: Vec<TimeEvent> = Vec::new();
while self
.timer_queue
.peek()
.is_some_and(|entry| entry.0.ts_event <= to_time_ns)
{
let entry = self
.timer_queue
.pop()
.expect("timer queue peeked Some but pop returned None");
let Some((event, next_event)) = self.advance_timer_from_entry(&entry.0) else {
continue;
};
events.push(event);
if let Some(next_event) = next_event {
self.timer_queue.push(next_event);
}
}
self.compact_timer_queue_if_needed();
if events.len() >= WARN_TIME_EVENTS_THRESHOLD {
log::warn!(
"Allocated {} time events during clock advancement from {} to {}, \
consider stopping the timer between large time ranges with no data points",
events.len().separate_with_commas(),View on GitHub (pinned to 18893faf8b)
Solutions
- Ensure no other thread/task mutates the timer queue while advance_time runs
- If a custom BinaryHeap/wrapper is in play, fix its pop/peek implementation
- Check for concurrent calls to advance_time or timer cancellation during iteration
- Report as a bug if reproducible with stock components — it is an internal invariant violation
Defensive patterns
Strategy: retry
Try / catch
// Internal invariant: on panic, capture state and report
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| clock.advance_time(to_ns)));
if result.is_err() {
eprintln!("timer queue invariant violated; rebuild timers");
} Prevention
- Never mutate the timer queue from multiple threads while advancing time
- Avoid cancelling timers reentrantly from timer callbacks
- Keep the stock BinaryHeap timer queue; report deviations as bugs
When it happens
Trigger: Calling advance_time (typically from tests that advance the TestClock/advance time past timer deadlines) where peek() reports a due entry (entry.0.ts_event <= to_time_ns) but pop() returns None.
Common situations: Custom timer queue implementations or heap wrappers with buggy behavior; concurrent mutation of the timer queue from another thread; test harnesses replacing the queue; heap comparator changes breaking ordering invariants.
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
- Event '{}' should have associated handler
- timer event sender was unset for Rust callback system
- Close command should not be drained
- Flush command should not be drained
- Order invariant violated: first event must be OrderInitializ
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/111979a2f3ddb61b.
Report an issue: GitHub.