{"record":{"id":"ed2915bb16133d00","repo":"clockworklabs/SpacetimeDB","slug":"duration-overflows-i64-microseconds","errorCode":null,"errorMessage":"Duration overflows i64 microseconds","messagePattern":"Duration overflows i64 microseconds","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/sats/src/time_duration.rs","lineNumber":78,"sourceCode":"    /// Converts `self` to `Duration`, clamping to 0 if negative.\n    pub fn to_duration_saturating(self) -> Duration {\n        self.to_duration().unwrap_or(Duration::ZERO)\n    }\n\n    /// Returns a positive `TimeDuration` with the magnitude of `self`.\n    pub fn abs(self) -> Self {\n        Self::from_micros(self.to_micros().saturating_abs())\n    }\n\n    /// Return a [`TimeDuration`] which represents the same span as `duration`.\n    ///\n    /// Panics if `duration.as_micros` overflows an `i64`\n    pub fn from_duration(duration: Duration) -> Self {\n        Self::from_micros(\n            duration\n                .as_micros()\n                .try_into()\n                .expect(\"Duration overflows i64 microseconds\"),\n        )\n    }\n\n    /// Returns `Some(self + other)`, or `None` if that value would be out of bounds for [`TimeDuration`].\n    pub fn checked_add(self, other: Self) -> Option<Self> {\n        self.to_micros().checked_add(other.to_micros()).map(Self::from_micros)\n    }\n\n    /// Returns `Some(self - other)`, or `None` if that value would be out of bounds for [`TimeDuration`].\n    pub fn checked_sub(self, other: Self) -> Option<Self> {\n        self.to_micros().checked_sub(other.to_micros()).map(Self::from_micros)\n    }\n\n    /// Generate an `iso8601` format string.\n    ///\n    /// This is the better supported format for use for the `pg wire protocol`.\n    ///\n    /// Example:","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/524b4487d949b61a07d4f39c862d1290259dfd20/crates/sats/src/time_duration.rs#L60-L96","documentation":"`TimeDuration` stores microseconds in an i64 (range about ±292,471 years). `from_duration` converts a std Duration and panics when `duration.as_micros()` (a u128) does not fit in i64. Realistically only unit-math bugs reach this — e.g. multiplying by 1_000_000 twice, or treating nanoseconds as microseconds — since no legitimate measured duration spans ~292k years.","triggerScenarios":"Calling `TimeDuration::from_duration(d)` where d was built by mistaken unit arithmetic (e.g. Duration::from_micros(nanos * 1_000_000)), or by accumulating durations in a loop without overflow checks until the value explodes.","commonSituations":"Timing/metrics code that multiplies the wrong unit constants; summing durations over long-lived processes; converting untrusted Duration input from wire formats or config.","solutions":["Fix the unit math producing the oversized Duration — usually a duplicated *1_000_000 factor or nanos/micros confusion.","Validate at the boundary: reject durations where `as_micros() > i64::MAX as u128` instead of converting.","If huge spans are legitimate, clamp with `min(i64::MAX as u128)` or use checked conversion and handle the error."],"exampleFix":"// before\nlet td = TimeDuration::from_duration(huge); // panics beyond ~292k years\n\n// after: validate, then convert explicitly\nif d.as_micros() > i64::MAX as u128 { return Err(\"duration too large\"); }\nlet td = TimeDuration::from_micros(d.as_micros() as i64);","handlingStrategy":"validation","validationCode":"fn fits_i64_micros(d: Duration) -> bool { d.as_micros() <= i64::MAX as u128 }\nif !fits_i64_micros(d) { return Err(\"duration too large\".into()); }\nlet td = TimeDuration::from_duration(d);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Centralize Duration construction; never hand-multiply unit constants.","Validate durations from external input before converting.","Use checked arithmetic when accumulating durations over long periods."],"tags":["rust","duration","overflow","spacetimedb-sats"],"backgroundTag":"duration-overflow","analyzedSha":"524b4487d949b61a07d4f39c862d1290259dfd20","analyzedAt":"2026-08-16T23:58:54.611Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}