{"record":{"id":"b4eb205d4ebc9f0e","repo":"clockworklabs/SpacetimeDB","slug":"duration-since-unix-epoch-overflows-i64-microsecon","errorCode":null,"errorMessage":"Duration since Unix epoch overflows i64 microseconds","messagePattern":"Duration since Unix epoch overflows i64 microseconds","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/sats/src/timestamp.rs","lineNumber":78,"sourceCode":"    /// Returns `Err(duration_before_unix_epoch)` if `self` is before `Self::UNIX_EPOCH`.\n    pub fn to_duration_since_unix_epoch(self) -> Result<Duration, Duration> {\n        let micros = self.to_micros_since_unix_epoch();\n        if micros >= 0 {\n            Ok(Duration::from_micros(micros as u64))\n        } else {\n            Err(Duration::from_micros((-micros) as u64))\n        }\n    }\n\n    /// Return a [`Timestamp`] which is [`Timestamp::UNIX_EPOCH`] plus `duration`.\n    ///\n    /// Panics if `duration.as_micros` overflows an `i64`\n    pub fn from_duration_since_unix_epoch(duration: Duration) -> Self {\n        Self::from_micros_since_unix_epoch(\n            duration\n                .as_micros()\n                .try_into()\n                .expect(\"Duration since Unix epoch overflows i64 microseconds\"),\n        )\n    }\n\n    /// Convert `self` into a [`SystemTime`] which refers to approximately the same point in time.\n    ///\n    /// This conversion may lose precision, as [`SystemTime`]'s prevision varies depending on platform.\n    /// E.g. Unix targets have microsecond precision, but Windows only 100-microsecond precision.\n    ///\n    /// This conversion may panic if `self` is out of bounds for [`SystemTime`].\n    /// We are not aware of any platforms for which [`SystemTime`] offers a smaller range than [`Timestamp`],\n    /// but such a platform may exist.\n    pub fn to_system_time(self) -> SystemTime {\n        match self.to_duration_since_unix_epoch() {\n            Ok(positive) => SystemTime::UNIX_EPOCH\n                .checked_add(positive)\n                .expect(\"Timestamp with i64 microseconds since Unix epoch overflows SystemTime\"),\n            Err(negative) => SystemTime::UNIX_EPOCH\n                .checked_sub(negative)","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/524b4487d949b61a07d4f39c862d1290259dfd20/crates/sats/src/timestamp.rs#L60-L96","documentation":"`Timestamp` is micros since the Unix epoch stored in an i64 (~±292 years around 1970). `from_duration_since_unix_epoch` panics when `duration.as_micros()` overflows i64 — i.e. a Duration longer than roughly 292,471 years. Normal wall-clock conversion never approaches this; the trigger is almost always a units bug or an attacker-controlled duration.","triggerScenarios":"Calling `Timestamp::from_duration_since_unix_epoch(d)` with d produced by wrong unit multipliers (nanos treated as micros and scaled again), unvalidated input, or runaway accumulation loops.","commonSituations":"Date arithmetic bugs in expiry/timeout logic; deserializing untrusted durations from API payloads or config; tests with exaggerated constants.","solutions":["Fix the unit arithmetic that produced the oversized Duration.","Validate external durations before converting: `d.as_micros() <= i64::MAX as u128`.","Use `try_into()` on the micros and handle the Err as a proper error value instead of panicking."],"exampleFix":"// before\nlet ts = Timestamp::from_duration_since_unix_epoch(Duration::from_secs(60 * 60 * 24 * 365 * 300_000)); // panics\n\n// after: checked conversion the caller can handle\nlet micros: i64 = duration.as_micros().try_into().map_err(|_| \"duration out of range\")?;\nlet ts = Timestamp::from_micros_since_unix_epoch(micros);","handlingStrategy":"validation","validationCode":"if duration.as_micros() > i64::MAX as u128 {\n    return Err(\"duration exceeds Timestamp range\".into());\n}\nlet ts = Timestamp::from_duration_since_unix_epoch(duration);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Fix unit arithmetic at the source; i64-micros overflow implies a bug or hostile input.","Range-check untrusted durations at the trust boundary.","Prefer Timestamp::from_micros_since_unix_epoch with a checked cast so errors are values."],"tags":["rust","timestamp","overflow","spacetimedb-sats"],"backgroundTag":"timestamp-overflow","analyzedSha":"524b4487d949b61a07d4f39c862d1290259dfd20","analyzedAt":"2026-08-16T23:58:54.611Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}