bevyengine/bevy · critical

overflow when subtracting duration from instant

Error message

overflow when subtracting duration from instant

What it means

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.

Source

Thrown at crates/bevy_platform/src/time/fallback.rs:125

    /// underlying data structure. See [`Instant::checked_add`] for a version without panic.
    fn add(self, other: Duration) -> Instant {
        self.checked_add(other)
            .expect("overflow when adding duration to instant")
    }
}

impl AddAssign<Duration> for Instant {
    fn add_assign(&mut self, other: Duration) {
        *self = *self + other;
    }
}

impl Sub<Duration> for Instant {
    type Output = Instant;

    fn sub(self, other: Duration) -> Instant {
        self.checked_sub(other)
            .expect("overflow when subtracting duration from instant")
    }
}

impl SubAssign<Duration> for Instant {
    fn sub_assign(&mut self, other: Duration) {
        *self = *self - other;
    }
}

impl Sub<Instant> for Instant {
    type Output = Duration;

    /// Returns the amount of time elapsed from another instant to this one,
    /// or zero duration if that instant is later than this one.
    fn sub(self, other: Instant) -> Duration {
        self.duration_since(other)
    }
}

View on GitHub (pinned to 396ca72708)

Solutions

  1. Use `instant.checked_sub(duration)` and handle `None` (clamp to the earliest representable instant)
  2. Restructure to compare instants (`if now >= deadline`) instead of subtracting large durations
  3. Validate/clamp incoming durations before arithmetic

Example fix

// before: panics when duration exceeds the counter value
let window_start = now - lookback;

// after: clamp on underflow
let window_start = now.checked_sub(lookback).unwrap_or(earliest_instant);
Defensive patterns

Strategy: validation

Validate before calling

let window_start = now
    .checked_sub(lookback)
    .unwrap_or(earliest_representable_instant); // clamp instead of panicking

Prevention

When it happens

Trigger: `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.

Common situations: 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).

Related errors


AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20). Data as JSON: /api/errors/c6b1a1ce9f562586. Report an issue: GitHub.