{"record":{"id":"76e263feda2911db","repo":"nautechsystems/nautilus_trader","slug":"duration-is-longer-than-584-years","errorCode":null,"errorMessage":"Duration is longer than 584 years","messagePattern":"Duration is longer than 584 years","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/network/src/ratelimiter/nanos.rs","lineNumber":68,"sourceCode":"\n    #[inline]\n    pub const fn saturating_add(self, rhs: Self) -> Self {\n        Self(self.0.saturating_add(rhs.0))\n    }\n\n    #[inline]\n    pub const fn saturating_mul(self, rhs: u64) -> Self {\n        Self(self.0.saturating_mul(rhs))\n    }\n}\n\nimpl From<Duration> for Nanos {\n    fn from(d: Duration) -> Self {\n        // This will panic:\n        Self(\n            d.as_nanos()\n                .try_into()\n                .expect(\"Duration is longer than 584 years\"),\n        )\n    }\n}\n\nimpl Debug for Nanos {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {\n        let d = Duration::from_nanos(self.0);\n        write!(f, \"Nanos({d:?})\")\n    }\n}\n\n// Add and Mul saturate: release builds disable overflow checks, and a wrapped\n// TAT would admit every request; pinning at the far future denies instead.\nimpl Add<Self> for Nanos {\n    type Output = Self;\n\n    fn add(self, rhs: Self) -> Self::Output {\n        Self(self.0.saturating_add(rhs.0))","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/network/src/ratelimiter/nanos.rs#L50-L86","documentation":"The From<Duration> for Nanos conversion in the rate limiter stores time as u64 nanoseconds (governor-style). Any Duration longer than ~584 years cannot fit and the expect panics. This mirrors the underlying u64 nanosecond representation used by the GCRA algorithm.","triggerScenarios":"Converting a Duration into Nanos via .into()/From where d.as_nanos() > u64::MAX — e.g. building a Quota with an astronomically large replenish interval, or passing a bad Duration into rate limiter APIs.","commonSituations":"Configuration mistakes like a period parsed in the wrong unit (seconds value fed as nanoseconds), computed intervals from overflowing arithmetic, or Duration::MAX used as 'never'.","solutions":["Clamp or validate the Duration before conversion (as_nanos() <= u64::MAX).","Fix the unit conversion at the source of the Duration.","Represent 'unlimited/never' with the library's dedicated constructs rather than a giant Duration."],"exampleFix":"// before\nlet nanos: Nanos = my_duration.into(); // panics for > 584 years\n// after\nassert!(my_duration.as_nanos() <= u64::MAX as u128, \"duration exceeds Nanos range\");\nlet nanos: Nanos = my_duration.into();","handlingStrategy":"validation","validationCode":"fn to_nanos_safe(d: Duration) -> Option<u64> {\n    u64::try_from(d.as_nanos()).ok()\n}","typeGuard":"fn fits_nanos(d: &Duration) -> bool { d.as_nanos() <= u64::MAX as u128 }","tryCatchPattern":"let result = std::panic::catch_unwind(|| { let n: Nanos = d.into(); n });","preventionTips":["Validate durations from config before converting to Nanos","Use explicit u64::try_from(as_nanos()) to get an error instead of a panic","Audit unit handling when building Durations from user config"],"tags":["rust","duration","overflow","rate-limiting"],"backgroundTag":"invalid-duration-format","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"}