{"record":{"id":"c7e80ce066ec7ddb","repo":"bevyengine/bevy","slug":"overflow-when-adding-duration-to-instant","errorCode":null,"errorMessage":"overflow when adding duration to instant","messagePattern":"overflow when adding duration to instant","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/bevy_platform/src/time/fallback.rs","lineNumber":110,"sourceCode":"\n    /// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as\n    /// `Instant` (which means it's inside the bounds of the underlying data structure), `None`\n    /// otherwise.\n    pub fn checked_sub(&self, duration: Duration) -> Option<Instant> {\n        self.0.checked_sub(duration).map(Instant)\n    }\n}\n\nimpl Add<Duration> for Instant {\n    type Output = Instant;\n\n    /// # Panics\n    ///\n    /// This function may panic if the resulting point in time cannot be represented by the\n    /// 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","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/bevyengine/bevy/blob/396ca727080776bd313bb892423b7d94e03b81b4/crates/bevy_platform/src/time/fallback.rs#L92-L128","documentation":"bevy_platform's fallback `Instant` (no_std time) wraps a tick-count `Duration`. `impl Add<Duration> for Instant` uses `checked_add` and `.expect(\"overflow when adding duration to instant\")` (crates/bevy_platform/src/time/fallback.rs:110), mirroring std's panic-on-overflow contract: if the resulting counter value cannot be represented, the add panics.","triggerScenarios":"`instant + duration` where the sum overflows the internal u64 nanosecond counter — practically this needs a Duration near `u64::MAX` nanoseconds (~584 years), e.g. `Instant::now() + Duration::MAX`, a `from_secs(u64::MAX)` from misparsed config, or saturated duration accumulation in a timer loop.","commonSituations":"Timeout/deadline math built from unchecked config values; `saturating_add`ed durations accumulating over long runs; unit tests using absurd durations against the fallback clock.","solutions":["Use `instant.checked_add(duration)` (or `saturating_add` where appropriate) instead of `+`","Clamp durations from config/network before deadline math: `d.min(Duration::from_secs(86400))`","Validate parsed timeout values at load time"],"exampleFix":"// before: panics if duration is huge\nlet deadline = start + timeout;\n\n// after: handle the unrepresentable case\nlet deadline = start.checked_add(timeout).unwrap_or_else(|| start + Duration::from_secs(3600));","handlingStrategy":"validation","validationCode":"let deadline = start\n    .checked_add(timeout)\n    .ok_or(TimeoutTooLarge)?; // or clamp:\n// let deadline = start.checked_add(timeout).unwrap_or(start + Duration::from_secs(3600));","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Prefer checked_add/saturating_add in all deadline math","Clamp timeouts parsed from config: d.min(Duration::from_secs(60 * 60 * 24))","Unit-test clock math with Duration::MAX to catch panics before release"],"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-14T00:17:10.932Z"}