clockworklabs/SpacetimeDB · error
Duration overflows i64 microseconds
Error message
Duration overflows i64 microseconds
What it means
`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.
Source
Thrown at crates/sats/src/time_duration.rs:78
/// Converts `self` to `Duration`, clamping to 0 if negative.
pub fn to_duration_saturating(self) -> Duration {
self.to_duration().unwrap_or(Duration::ZERO)
}
/// Returns a positive `TimeDuration` with the magnitude of `self`.
pub fn abs(self) -> Self {
Self::from_micros(self.to_micros().saturating_abs())
}
/// Return a [`TimeDuration`] which represents the same span as `duration`.
///
/// Panics if `duration.as_micros` overflows an `i64`
pub fn from_duration(duration: Duration) -> Self {
Self::from_micros(
duration
.as_micros()
.try_into()
.expect("Duration overflows i64 microseconds"),
)
}
/// Returns `Some(self + other)`, or `None` if that value would be out of bounds for [`TimeDuration`].
pub fn checked_add(self, other: Self) -> Option<Self> {
self.to_micros().checked_add(other.to_micros()).map(Self::from_micros)
}
/// Returns `Some(self - other)`, or `None` if that value would be out of bounds for [`TimeDuration`].
pub fn checked_sub(self, other: Self) -> Option<Self> {
self.to_micros().checked_sub(other.to_micros()).map(Self::from_micros)
}
/// Generate an `iso8601` format string.
///
/// This is the better supported format for use for the `pg wire protocol`.
///
/// Example:View on GitHub (pinned to 524b4487d9)
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.
Example fix
// before
let td = TimeDuration::from_duration(huge); // panics beyond ~292k years
// after: validate, then convert explicitly
if d.as_micros() > i64::MAX as u128 { return Err("duration too large"); }
let td = TimeDuration::from_micros(d.as_micros() as i64); Defensive patterns
Strategy: validation
Validate before calling
fn fits_i64_micros(d: Duration) -> bool { d.as_micros() <= i64::MAX as u128 }
if !fits_i64_micros(d) { return Err("duration too large".into()); }
let td = TimeDuration::from_duration(d); Prevention
- Centralize Duration construction; never hand-multiply unit constants.
- Validate durations from external input before converting.
- Use checked arithmetic when accumulating durations over long periods.
When it happens
Trigger: 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.
Common situations: Timing/metrics code that multiplies the wrong unit constants; summing durations over long-lived processes; converting untrusted Duration input from wire formats or config.
Related errors
- Duration since Unix epoch overflows i64 microseconds
- Timestamp with i64 microseconds since Unix epoch overflows S
- Timestamp with i64 microseconds before Unix epoch overflows
- SystemTime predates the Unix epoch
- timestamp before unix epoch
AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16).
Data as JSON: /api/errors/ed2915bb16133d00.
Report an issue: GitHub.