{"record":{"id":"9bf6806ffbce1e6e","repo":"nautechsystems/nautilus_trader","slug":"atomictime-overflow-reached-u64-max","errorCode":null,"errorMessage":"AtomicTime overflow: reached u64::MAX","messagePattern":"AtomicTime overflow: reached u64::MAX","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/core/src/time.rs","lineNumber":337,"sourceCode":"    ///\n    /// # Panics\n    ///\n    /// Panics if the internal counter has reached `u64::MAX`, which would indicate the process has\n    /// been running for longer than the representable range (~584 years) *or* the clock was\n    /// manually corrupted.\n    pub fn time_since_epoch(&self) -> UnixNanos {\n        // This method guarantees strict consistency but may incur a performance cost under\n        // high contention due to retries in the `compare_exchange` loop.\n        let now = nanos_since_unix_epoch();\n\n        loop {\n            // Acquire to observe the latest stored value\n            let last = self.timestamp_ns.load(Ordering::Acquire);\n\n            // Ensure we never wrap past u64::MAX - treat that as a fatal error\n            let incremented = last\n                .checked_add(1)\n                .expect(\"AtomicTime overflow: reached u64::MAX\");\n            let next = now.max(incremented);\n\n            // AcqRel on success ensures this new value is published,\n            // Acquire on failure reloads if we lost a CAS race.\n            //\n            // Note that under heavy contention (many threads calling this in tight loops),\n            // the CAS loop may increase latency.\n            //\n            // However, in practice, the loop terminates quickly because:\n            // - System time naturally advances between iterations\n            // - Each iteration increments time by at least 1ns, preventing ABA problems\n            // - True contention requiring retry is rare in normal usage patterns\n            //\n            // The concurrent stress test (4 threads × 100k iterations) validates this approach.\n            if self\n                .timestamp_ns\n                .compare_exchange(last, next, Ordering::AcqRel, Ordering::Acquire)\n                .is_ok()","sourceCodeStart":319,"sourceCodeEnd":355,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/core/src/time.rs#L319-L355","documentation":"AtomicTime::time_since_epoch increments a stored u64 nanosecond timestamp on every call and refuses to wrap past u64::MAX. `checked_add(1)` returns None at the maximum value, so the `.expect` panics. This is a deliberate fatal-error guard: wrapping a monotonic clock would silently produce times in the past.","triggerScenarios":"Calling `time_since_epoch()` when the internal AtomicTime counter already holds u64::MAX. In practice this only happens under the `NautilusClock`/test-clock mode where every clock read bumps the counter: billions of calls to `get_time_ns` in one process, or a unit test deliberately driving the counter to overflow.","commonSituations":"Long-running simulations or backtests with a test clock that call the clock in a tight loop for months of simulated nanoseconds; fuzz tests; accidental use of AtomicTime as a general-purpose counter.","solutions":["Reduce per-tick clock reads by caching `get_time_ns()` results per event batch instead of calling it per field","Use the real system clock (time is derived from the OS) rather than the incrementing test-clock counter for very long runs","Restart the process/clock before the counter can reach u64::MAX (about 584 years of nanoseconds, so practically only reachable in test-clock mode)","If hit in tests, restructure the test to not drive AtomicTime to saturation"],"exampleFix":"// before: per-field clock reads in a hot loop\nfor tick in ticks {\n    let ts = clock.get_time_ns(); // bumps AtomicTime each call\n    process(tick, ts);\n}\n// after: one read per batch\nlet ts = clock.get_time_ns();\nfor tick in ticks {\n    process(tick, ts);\n}","handlingStrategy":"validation","validationCode":"// Rust: before long test-clock loops, bound the number of clock reads\nconst MAX_CLOCK_READS: u128 = (u64::MAX as u128) - 1_000_000;\nassert!(clock_reads_estimate < MAX_CLOCK_READS, \"would exhaust AtomicTime counter\");","typeGuard":null,"tryCatchPattern":"// These are panics, not Results; only recoverable with catch_unwind\nlet result = std::panic::catch_unwind(|| clock.get_time_ns());","preventionTips":["Cache clock reads per batch instead of calling get_time_ns per field","Keep per-call increments (test-clock mode) out of hot loops","Never use AtomicTime as a general-purpose counter"],"tags":["rust","panic","integer-overflow","clock"],"backgroundTag":"value-out-of-range","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}