clockworklabs/SpacetimeDB · error

Duration since Unix epoch overflows i64 microseconds

Error message

Duration since Unix epoch overflows i64 microseconds

What it means

`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.

Source

Thrown at crates/sats/src/timestamp.rs:78

    /// Returns `Err(duration_before_unix_epoch)` if `self` is before `Self::UNIX_EPOCH`.
    pub fn to_duration_since_unix_epoch(self) -> Result<Duration, Duration> {
        let micros = self.to_micros_since_unix_epoch();
        if micros >= 0 {
            Ok(Duration::from_micros(micros as u64))
        } else {
            Err(Duration::from_micros((-micros) as u64))
        }
    }

    /// Return a [`Timestamp`] which is [`Timestamp::UNIX_EPOCH`] plus `duration`.
    ///
    /// Panics if `duration.as_micros` overflows an `i64`
    pub fn from_duration_since_unix_epoch(duration: Duration) -> Self {
        Self::from_micros_since_unix_epoch(
            duration
                .as_micros()
                .try_into()
                .expect("Duration since Unix epoch overflows i64 microseconds"),
        )
    }

    /// Convert `self` into a [`SystemTime`] which refers to approximately the same point in time.
    ///
    /// This conversion may lose precision, as [`SystemTime`]'s prevision varies depending on platform.
    /// E.g. Unix targets have microsecond precision, but Windows only 100-microsecond precision.
    ///
    /// This conversion may panic if `self` is out of bounds for [`SystemTime`].
    /// We are not aware of any platforms for which [`SystemTime`] offers a smaller range than [`Timestamp`],
    /// but such a platform may exist.
    pub fn to_system_time(self) -> SystemTime {
        match self.to_duration_since_unix_epoch() {
            Ok(positive) => SystemTime::UNIX_EPOCH
                .checked_add(positive)
                .expect("Timestamp with i64 microseconds since Unix epoch overflows SystemTime"),
            Err(negative) => SystemTime::UNIX_EPOCH
                .checked_sub(negative)

View on GitHub (pinned to 524b4487d9)

Solutions

  1. Fix the unit arithmetic that produced the oversized Duration.
  2. Validate external durations before converting: `d.as_micros() <= i64::MAX as u128`.
  3. Use `try_into()` on the micros and handle the Err as a proper error value instead of panicking.

Example fix

// before
let ts = Timestamp::from_duration_since_unix_epoch(Duration::from_secs(60 * 60 * 24 * 365 * 300_000)); // panics

// after: checked conversion the caller can handle
let micros: i64 = duration.as_micros().try_into().map_err(|_| "duration out of range")?;
let ts = Timestamp::from_micros_since_unix_epoch(micros);
Defensive patterns

Strategy: validation

Validate before calling

if duration.as_micros() > i64::MAX as u128 {
    return Err("duration exceeds Timestamp range".into());
}
let ts = Timestamp::from_duration_since_unix_epoch(duration);

Prevention

When it happens

Trigger: 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.

Common situations: Date arithmetic bugs in expiry/timeout logic; deserializing untrusted durations from API payloads or config; tests with exaggerated constants.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16). Data as JSON: /api/errors/b4eb205d4ebc9f0e. Report an issue: GitHub.