{"record":{"id":"c6b1a1ce9f562586","repo":"bevyengine/bevy","slug":"overflow-when-subtracting-duration-from-instant","errorCode":null,"errorMessage":"overflow when subtracting duration from instant","messagePattern":"overflow when subtracting duration from instant","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/bevy_platform/src/time/fallback.rs","lineNumber":125,"sourceCode":"    /// underlying data structure. See [`Instant::checked_add`] for a version without panic.\n    fn add(self, other: Duration) -> Instant {\n        self.checked_add(other)\n            .expect(\"overflow when adding duration to instant\")\n    }\n}\n\nimpl AddAssign<Duration> for Instant {\n    fn add_assign(&mut self, other: Duration) {\n        *self = *self + other;\n    }\n}\n\nimpl Sub<Duration> for Instant {\n    type Output = Instant;\n\n    fn sub(self, other: Duration) -> Instant {\n        self.checked_sub(other)\n            .expect(\"overflow when subtracting duration from instant\")\n    }\n}\n\nimpl SubAssign<Duration> for Instant {\n    fn sub_assign(&mut self, other: Duration) {\n        *self = *self - other;\n    }\n}\n\nimpl Sub<Instant> for Instant {\n    type Output = Duration;\n\n    /// Returns the amount of time elapsed from another instant to this one,\n    /// or zero duration if that instant is later than this one.\n    fn sub(self, other: Instant) -> Duration {\n        self.duration_since(other)\n    }\n}","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/bevyengine/bevy/blob/396ca727080776bd313bb892423b7d94e03b81b4/crates/bevy_platform/src/time/fallback.rs#L107-L143","documentation":"The fallback `Instant`'s `Sub<Duration>` impl (crates/bevy_platform/src/time/fallback.rs:123) uses `checked_sub` and panics with \"overflow when subtracting duration from instant\" when the subtracted duration exceeds the internal counter value — i.e. computing an instant before the clock's zero point. `SubAssign` builds on it, so `-=` panics the same way.","triggerScenarios":"`instant - duration` where `duration` is larger than the counter (e.g. subtracting hours from a freshly initialized/set elapsed counter, or `Duration::MAX`); computing \"start minus lookahead\" with unvalidated durations; `instant -= d` accumulation loops that go negative.","commonSituations":"Schedule/window arithmetic that assumes a large epoch; code ported from std where the counter happened to be huge (rdtsc-based counters are large, so this bites mainly right after `set_elapsed` with small counters or with pathological durations).","solutions":["Use `instant.checked_sub(duration)` and handle `None` (clamp to the earliest representable instant)","Restructure to compare instants (`if now >= deadline`) instead of subtracting large durations","Validate/clamp incoming durations before arithmetic"],"exampleFix":"// before: panics when duration exceeds the counter value\nlet window_start = now - lookback;\n\n// after: clamp on underflow\nlet window_start = now.checked_sub(lookback).unwrap_or(earliest_instant);","handlingStrategy":"validation","validationCode":"let window_start = now\n    .checked_sub(lookback)\n    .unwrap_or(earliest_representable_instant); // clamp instead of panicking","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use checked_sub and define an explicit clamp target for underflow","Compare instants (`now >= deadline`) rather than subtracting large durations","Validate lookback/window durations from external sources"],"tags":["bevy","time","duration","overflow","panic","no-std"],"backgroundTag":"time-arithmetic-overflow","analyzedSha":"396ca727080776bd313bb892423b7d94e03b81b4","analyzedAt":"2026-08-20T16:12:39.808Z","contentChangedAt":"2026-08-20T16:12:39.808Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}