{"record":{"id":"111979a2f3ddb61b","repo":"nautechsystems/nautilus_trader","slug":"timer-queue-peeked-some-but-pop-returned-none","errorCode":null,"errorMessage":"timer queue peeked Some but pop returned None","messagePattern":"timer queue peeked Some but pop returned None","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/common/src/clock.rs","lineNumber":959,"sourceCode":"            to_time_ns >= from_time_ns,\n            \"Invariant: time must be non-decreasing, `to_time_ns` {to_time_ns} < `from_time_ns` {from_time_ns}\"\n        );\n\n        if set_time {\n            self.time.set_time(to_time_ns);\n        }\n\n        let mut events: Vec<TimeEvent> = Vec::new();\n\n        while self\n            .timer_queue\n            .peek()\n            .is_some_and(|entry| entry.0.ts_event <= to_time_ns)\n        {\n            let entry = self\n                .timer_queue\n                .pop()\n                .expect(\"timer queue peeked Some but pop returned None\");\n\n            let Some((event, next_event)) = self.advance_timer_from_entry(&entry.0) else {\n                continue;\n            };\n\n            events.push(event);\n            if let Some(next_event) = next_event {\n                self.timer_queue.push(next_event);\n            }\n        }\n\n        self.compact_timer_queue_if_needed();\n\n        if events.len() >= WARN_TIME_EVENTS_THRESHOLD {\n            log::warn!(\n                \"Allocated {} time events during clock advancement from {} to {}, \\\n                 consider stopping the timer between large time ranges with no data points\",\n                events.len().separate_with_commas(),","sourceCodeStart":941,"sourceCodeEnd":977,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/common/src/clock.rs#L941-L977","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Internal invariant: on panic, capture state and report\nlet result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| clock.advance_time(to_ns)));\nif result.is_err() {\n    eprintln!(\"timer queue invariant violated; rebuild timers\");\n}","preventionTips":["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"],"tags":["rust","panic","timer","invariant"],"backgroundTag":"internal-invariant-violation","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}