{"record":{"id":"4581b4c3cfd50203","repo":"clockworklabs/SpacetimeDB","slug":"timestamp-before-unix-epoch","errorCode":null,"errorMessage":"timestamp before unix epoch","messagePattern":"timestamp before unix epoch","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/sats/src/uuid.rs","lineNumber":133,"sourceCode":"    /// let counter = std::cell::Cell::new(1);\n    /// // Use the `ReducerContext::rng()` | `ProcedureContext::rng()` to generate random bytes,\n    /// // or call `ReducerContext::new_uuid_v7()` / `ProcedureContext::new_uuid_v7()`\n    /// let random_bytes = [0u8; 4];\n    /// let uuid = Uuid::from_counter_v7(&counter, now, &random_bytes).unwrap();\n    ///\n    /// assert_eq!(\n    ///     \"0000647e-5180-7000-8000-000200000000\",\n    ///     uuid.to_string(),\n    /// );\n    /// ```\n    pub fn from_counter_v7(counter: &Cell<u32>, now: Timestamp, random_bytes: &[u8; 4]) -> anyhow::Result<Self> {\n        // Monotonic counter value (31 bits)\n        let counter_val = counter.get();\n        counter.set(counter_val.wrapping_add(1) & 0x7FFF_FFFF);\n\n        let ts_ms = now\n            .to_duration_since_unix_epoch()\n            .expect(\"timestamp before unix epoch\")\n            .as_millis() as i64\n            & 0xFFFFFFFFFFFF;\n\n        let mut bytes = [0u8; 16];\n\n        // unix_ts_ms (48 bits)\n        bytes[0] = (ts_ms >> 40) as u8;\n        bytes[1] = (ts_ms >> 32) as u8;\n        bytes[2] = (ts_ms >> 24) as u8;\n        bytes[3] = (ts_ms >> 16) as u8;\n        bytes[4] = (ts_ms >> 8) as u8;\n        bytes[5] = ts_ms as u8;\n\n        // version & variant\n        // bytes[6] = uuid::Version::SortRand;\n        // bytes[8] = Variant::RFC4122\n\n        // Counter bits","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/524b4487d949b61a07d4f39c862d1290259dfd20/crates/sats/src/uuid.rs#L115-L151","documentation":"`Uuid::from_counter_v7` builds a UUIDv7 whose leading 48 bits are milliseconds since the Unix epoch, so the timestamp must be at/after 1970; the code calls `now.to_duration_since_unix_epoch().expect(\"timestamp before unix epoch\")`. A pre-epoch Timestamp therefore panics — in practice meaning the host clock (or a test-supplied Timestamp) reads before the epoch.","triggerScenarios":"Generating ids via from_counter_v7 while the system clock is set before 1970 (dead RTC, VM clone artifact, severe clock skew), or passing `Timestamp::from_micros_since_unix_epoch(negative)` in tests.","commonSituations":"Unsynchronized VMs/containers with broken clocks; embedded hosts with dead RTC batteries; tests that deliberately exercise pre-epoch timestamps.","solutions":["Sync the host clock (NTP/systemd-timesyncd) and verify `date` is sane.","In tests, use Timestamp::UNIX_EPOCH or later values.","If pre-epoch inputs are possible in your flow, gate the call on `now >= Timestamp::UNIX_EPOCH` and fail with a proper error or use a random UUID."],"exampleFix":"// before: panics when now < epoch\nlet id = Uuid::from_counter_v7(&counter, now, &random_bytes)?;\n\n// after: guard the clock before generating\nif now.to_duration_since_unix_epoch().is_err() {\n    return Err(anyhow::anyhow!(\"clock pre-dates Unix epoch; refusing to mint UUIDv7\"));\n}\nlet id = Uuid::from_counter_v7(&counter, now, &random_bytes)?;","handlingStrategy":"validation","validationCode":"if now.to_duration_since_unix_epoch().is_err() {\n    return Err(\"cannot build UUIDv7 with pre-epoch timestamp\".into());\n}\nlet id = Uuid::from_counter_v7(&counter, now, &random_bytes)?;","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Run NTP on hosts that mint ids.","Assert now >= Timestamp::UNIX_EPOCH in tests before calling from_counter_v7.","Treat pre-epoch clocks as an environment fault: alert and refuse rather than panic."],"tags":["rust","uuid","uuidv7","clock","spacetimedb-sats"],"backgroundTag":"clock-before-epoch","analyzedSha":"524b4487d949b61a07d4f39c862d1290259dfd20","analyzedAt":"2026-08-16T23:58:54.611Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}