{"record":{"id":"0359a7db09ecedf7","repo":"vi/websocat","slug":"time-went-backwards","errorCode":null,"errorMessage":"Time went backwards","messagePattern":"Time went backwards","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/timestamp_peer.rs","lineNumber":63,"sourceCode":"struct TimestampWrapper(Box<dyn AsyncRead>, Option<Instant>);\n\nimpl Read for TimestampWrapper {\n    fn read(&mut self, b: &mut [u8]) -> Result<usize, IoError> {\n        let l = b.len();\n        assert!(l > 1);\n        let n = self.0.read(&mut b[..l])?;\n        if n == 0 {\n            return Ok(0);\n        }\n\n        let mut v: Vec<u8> = Vec::with_capacity(n + 50);\n        {\n            let mut vv = ::std::io::Cursor::new(&mut v);\n            use std::io::Write;\n            let x = if let Some(basetime) = self.1 {\n                Instant::now().duration_since(basetime).as_secs_f64()\n            } else {\n                (SystemTime::now().duration_since(UNIX_EPOCH).expect(\"Time went backwards\")).as_secs_f64()\n            };\n            let _ = write!(vv, \"{} \", x);\n            let _ = vv.write_all(&b[..n]);\n        }\n        \n        if v.len() > l {\n            warn!(\"Buffer too small, timstamp-prepended message may be truncated.\");\n        }\n        let ll = v.len().min(l);\n        b[..ll].copy_from_slice(&v[..ll]);\n        Ok(ll)\n    }\n}\nimpl AsyncRead for TimestampWrapper {}\n","sourceCodeStart":45,"sourceCodeEnd":78,"githubUrl":"https://github.com/vi/websocat/blob/3a3574cd2f5d17857d87f3982e72c3ede159dde0/src/timestamp_peer.rs#L45-L78","documentation":"When reading from this timestamping peer, the code stamps each chunk with the current time. If self.1 (a captured baseline Instant) is None, it converts SystemTime::now() from UNIX_EPOCH with an expect. On platforms where the wall clock is adjusted (NTP step, manual set, virtual machine resume, boot with RTC unset) SystemTime can appear to be before the epoch, and duration_since returns Err — the expect turns that into a panic aborting the read.","triggerScenarios":"System clock set to a time before 1970-01-01 (or the OS reporting such a time) while this peer reads data and no basetime was recorded; also triggered by clock stepping during container/VM start when the RTC is invalid.","commonSituations":"Embedded boards or VMs with dead RTC batteries booting with clock at 0 or negative-adjusted values; NTP forcing a large backwards step mid-session; Docker containers started before the host syncs its clock.","solutions":["Fix the system clock (run NTP/chrony/ntpd) so it is after the Unix epoch, then restart the process","Prefer the monotonic path: always seed self.1 with an Instant baseline at peer creation so the expect branch is never reached","Replace the expect with handling of the Err case, e.g. default to 0.0 seconds or return an io::ErrorKind::Other instead of panicking","Use Instant::now() exclusively for the timestamp if absolute wall-clock time is not required"],"exampleFix":"// before\n(SystemTime::now().duration_since(UNIX_EPOCH).expect(\"Time went backwards\")).as_secs_f64()\n// after\nSystemTime::now().duration_since(UNIX_EPOCH)\n    .unwrap_or_default() // clock before epoch: stamp 0.0 instead of panicking\n    .as_secs_f64()","handlingStrategy":"fallback","validationCode":"// check clock before starting timestamped reads\nlet ok = SystemTime::now().duration_since(UNIX_EPOCH).is_ok();\nif !ok { eprintln!(\"system clock is before the Unix epoch; fix time sync\"); }","typeGuard":"fn clock_is_sane() -> bool {\n    SystemTime::now().duration_since(UNIX_EPOCH).is_ok()\n}","tryCatchPattern":"// panic cannot be caught by Result; fence the read loop or fix the environment\nassert!(clock_is_sane(), \"clock before epoch — enable NTP before running\");\n// or run the reader under catch_unwind as a stopgap\nlet _ = std::panic::catch_unwind(AssertUnwindSafe(|| read_loop(&mut peer)));","preventionTips":["Run NTP/chrony so the wall clock never steps before the epoch","Boot embedded/VM systems with a valid RTC or an initial clock sync before starting the service","Prefer monotonic Instant-based stamping (seed the basetime) for durations","Patch the library to unwrap_or_default() instead of expect on duration_since"],"tags":["time","clock","panic","ntp"],"backgroundTag":"clock-went-backwards","analyzedSha":"3a3574cd2f5d17857d87f3982e72c3ede159dde0","analyzedAt":"2026-09-12T15:14:27.766Z","contentChangedAt":"2026-09-12T15:14:27.766Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}