nautechsystems/nautilus_trader · error
reservation index overflow
Error message
reservation index overflow
What it means
When a reservation would exceed the sliding-window limit, the code computes how many oldest timestamps must expire (needed) and indexes timestamps[needed - 1] to find when the request can proceed. Because used + weight > limit was checked with saturation and weight <= limit, needed >= 1 and fits in usize, so the expect should be unreachable. A panic indicates the arithmetic invariant was violated (e.g. u32 saturation aliasing with an enormous timestamp count).
Source
Thrown at crates/adapters/bybit/src/common/rate_limit.rs:170
if let Some(blocked_until) = window.blocked_until {
wait = wait.max(blocked_until.duration_since(now));
}
let limit = window.limit();
if reservation.weight > limit {
return Err(format!(
"Bybit request weight {} exceeds quota {} for {}",
reservation.weight,
limit,
reservation.key.label(),
));
}
let used = u32::try_from(window.timestamps.len()).unwrap_or(u32::MAX);
if used.saturating_add(reservation.weight) > limit {
let needed = used.saturating_add(reservation.weight) - limit;
let index =
usize::try_from(needed - 1).expect("reservation index overflow");
let ready_at = window.timestamps[index] + window.period;
wait = wait.max(ready_at.duration_since(now));
}
}
if wait.is_zero() {
for reservation in reservations {
let window = windows
.get_mut(reservation.key)
.expect("Bybit rate-limit window missing after planning");
window
.timestamps
.extend(std::iter::repeat_n(now, reservation.weight as usize));
}
}
wait
};View on GitHub (pinned to 18893faf8b)
Solutions
- Do not modify the order of the weight <= limit and used + weight > limit checks
- If you hit this, inspect window limit configuration for absurd values (limit near u32::MAX)
- Use checked arithmetic instead of saturating_add when refactoring this function
- Report with the configured limit/period values if it occurs in production
Example fix
// before
let used = u32::try_from(window.timestamps.len()).unwrap_or(u32::MAX);
// after
let used = u32::try_from(window.timestamps.len())
.expect("sliding window cannot hold more than u32::MAX timestamps"); Defensive patterns
Strategy: try-catch
Validate before calling
// check weight before calling the adapter assert!(request_weight <= configured_limit, "weight exceeds per-window quota");
Try / catch
// acquire returns Result; treat Err as backpressure, do not unwrap internals
match limiter.acquire(&reservations).await {
Ok(()) => send_request().await?,
Err(e) => log::warn!("rate limit rejected request: {e}"),
} Prevention
- Configure realistic Bybit limits (not near u32::MAX)
- Do not modify the check ordering inside acquire() when customizing
- Keep request weights within documented venue quotas
When it happens
Trigger: Only if window.timestamps grows beyond u32::MAX entries (u32::try_from fails, used becomes u32::MAX) making needed - 1 impossible, or if the weight/limit guard above is changed. Not reachable with realistic Bybit quotas.
Common situations: Effectively only hit by maintainers modifying the acquire() logic, e.g. reordering the weight check or changing types; not by library users.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Bybit rate-limit window missing after planning
- Close command should not be drained
- Flush command should not be drained
- Order invariant violated: first event must be OrderInitializ
- validated proxy target hash
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/045a21b7bea26ec2.
Report an issue: GitHub.