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
- 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
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
- 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
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
- overflow when adding duration to instant
- An elapsed time getter has not been provided to `Instant`. P
- array layout should be valid
- The TimeSender channel should always be empty during render.
- Cannot call `ReflectComponent::reflect_mut` on component {na
AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20).
Data as JSON: /api/errors/c6b1a1ce9f562586.
Report an issue: GitHub.