clockworklabs/SpacetimeDB · error
timestamp before unix epoch
Error message
timestamp before unix epoch
What it means
Panic in `Uuid::from_counter_v7`, SpacetimeDB's UUIDv7 generator. It converts the `now: Timestamp` argument via `to_duration_since_unix_epoch().expect("timestamp before unix epoch")`; a Timestamp earlier than 1970 returns `Err` (the Err encodes the pre-epoch offset) and the expect panics. UUIDv7 embeds a positive millisecond timestamp, so pre-epoch times are unsupported by construction.
Source
Thrown at crates/sats/src/uuid.rs:133
/// let counter = std::cell::Cell::new(1);
/// // Use the `ReducerContext::rng()` | `ProcedureContext::rng()` to generate random bytes,
/// // or call `ReducerContext::new_uuid_v7()` / `ProcedureContext::new_uuid_v7()`
/// let random_bytes = [0u8; 4];
/// let uuid = Uuid::from_counter_v7(&counter, now, &random_bytes).unwrap();
///
/// assert_eq!(
/// "0000647e-5180-7000-8000-000200000000",
/// uuid.to_string(),
/// );
/// ```
pub fn from_counter_v7(counter: &Cell<u32>, now: Timestamp, random_bytes: &[u8; 4]) -> anyhow::Result<Self> {
// Monotonic counter value (31 bits)
let counter_val = counter.get();
counter.set(counter_val.wrapping_add(1) & 0x7FFF_FFFF);
let ts_ms = now
.to_duration_since_unix_epoch()
.expect("timestamp before unix epoch")
.as_millis() as i64
& 0xFFFFFFFFFFFF;
let mut bytes = [0u8; 16];
// unix_ts_ms (48 bits)
bytes[0] = (ts_ms >> 40) as u8;
bytes[1] = (ts_ms >> 32) as u8;
bytes[2] = (ts_ms >> 24) as u8;
bytes[3] = (ts_ms >> 16) as u8;
bytes[4] = (ts_ms >> 8) as u8;
bytes[5] = ts_ms as u8;
// version & variant
// bytes[6] = uuid::Version::SortRand;
// bytes[8] = Variant::RFC4122
// Counter bitsView on GitHub (pinned to 6dee26c6ef)
Solutions
- Check and fix the system clock (enable NTP; verify `date -u` shows post-1970) — the clock, not the code, is usually wrong.
- Guard ID-generation call sites: `if now.to_duration_since_unix_epoch().is_err() { use fallback }` or clamp `now` to `Timestamp::UNIX_EPOCH`.
- Validate any externally-supplied Timestamp before passing it to `from_counter_v7`.
- In tests, use realistic fixed timestamps (post-1970) rather than epoch-relative negatives.
Example fix
// before: panics when clock/ts predates 1970
let id = Uuid::from_counter_v7(&counter, now, &rand)?;
// after: clamp pre-epoch times to the epoch
let now = if now.to_duration_since_unix_epoch().is_err() { Timestamp::UNIX_EPOCH } else { now };
let id = Uuid::from_counter_v7(&counter, now, &rand)?; Defensive patterns
Strategy: validation
Validate before calling
fn uuid_now(now: Timestamp, counter: &Cell<u32>, rnd: &[u8; 4]) -> anyhow::Result<Uuid> {
let now = if now.to_duration_since_unix_epoch().is_err() {
Timestamp::UNIX_EPOCH // clamp: UUIDv7 cannot encode pre-epoch
} else { now };
Ok(Uuid::from_counter_v7(counter, now, rnd)?)
} Try / catch
let r = std::panic::catch_unwind(AssertUnwindSafe(|| Uuid::from_counter_v7(&c, now, &rnd)));
match r { Ok(v) => v?, Err(_) => return Err(ClockError::pre_epoch) } Prevention
- Enable NTP and verify the host clock reads post-1970 before relying on ID generation.
- Validate externally-supplied Timestamps before passing them to UUID generation.
- In tests, use fixed post-epoch timestamps only.
When it happens
Trigger: Generating a UUIDv7 while the system clock (or the injected `now` value) reads before 1970-01-01: broken RTC/VM clock, NTP step-back in tests, or code passing a deliberately negative Timestamp (e.g. `UNIX_EPOCH - x` arithmetic) into ID generation.
Common situations: Machines booting with dead CMOS batteries into pre-1970 dates; sandboxes/containers with clock skew; test fixtures constructing fixed Timestamps below the epoch; feeding historical dates into ID generation by mistake.
Related errors
- SystemTime predates the Unix epoch
- Duration since Unix epoch overflows i64 microseconds
- Timestamp with i64 microseconds since Unix epoch overflows S
- Timestamp with i64 microseconds before Unix epoch overflows
- timestamp before unix epoch
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/4581b4c3cfd50203.
Report an issue: GitHub.