nautechsystems/nautilus_trader · error
Failed to subtract {n} years from {datetime}: month count ov
Error message
Failed to subtract {n} years from {datetime}: month count overflow What it means
subtract_n_years converts n years into a month count via checked_mul(12); if the product exceeds u32 the subtraction cannot be represented and this error is raised. Like the add variant, it prevents silent wraparound in month arithmetic.
Source
Thrown at crates/core/src/datetime.rs:622
///
/// Returns an error if the resulting date would be invalid or out of range.
pub fn add_n_years(datetime: Timestamp, n: u32) -> anyhow::Result<Timestamp> {
let months = n.checked_mul(12).ok_or_else(|| {
anyhow::anyhow!("Failed to add {n} years to {datetime}: month count overflow")
})?;
shift_months(datetime, i64::from(months))
.map_err(|_| anyhow::anyhow!("Failed to add {n} years to {datetime}"))
}
/// Subtract `n` years from a Jiff [`Timestamp`].
///
/// # Errors
///
/// Returns an error if the resulting date would be invalid or out of range.
pub fn subtract_n_years(datetime: Timestamp, n: u32) -> anyhow::Result<Timestamp> {
let months = n.checked_mul(12).ok_or_else(|| {
anyhow::anyhow!("Failed to subtract {n} years from {datetime}: month count overflow")
})?;
shift_months(datetime, -i64::from(months))
.map_err(|_| anyhow::anyhow!("Failed to subtract {n} years from {datetime}"))
}
/// Add `n` years to a given UNIX nanoseconds timestamp.
///
/// # Errors
///
/// Returns an error if the resulting timestamp is out of range or invalid.
pub fn add_n_years_nanos(unix_nanos: UnixNanos, n: u32) -> anyhow::Result<UnixNanos> {
let datetime = unix_nanos.to_datetime_utc();
let result = add_n_years(datetime, n)?;
let timestamp = result.as_nanosecond();
let nanos =
u64::try_from(timestamp).map_err(|_| anyhow::anyhow!("Negative timestamp not allowed"))?;View on GitHub (pinned to 18893faf8b)
Solutions
- Use a sane year offset (n <= 357,913,941; practically small).
- Validate n at the boundary before calling subtract_n_years.
- Fix upstream parsing that produced the unbounded value.
Example fix
// before
let ts = subtract_n_years(now, n).unwrap();
// after
anyhow::ensure!(n <= 100_000, "year offset too large: {n}");
let ts = subtract_n_years(now, n)?; Defensive patterns
Strategy: validation
Validate before calling
fn valid_year_offset(n: u32) -> bool { n <= 357_913_941 }
// reject before calling subtract_n_years Type guard
fn is_sane_years(n: u32) -> bool { n <= 10_000 } Prevention
- Bound lookback/year values at the config layer.
- Test any code path that can feed unbounded u32 values into date math.
When it happens
Trigger: Calling subtract_n_years(datetime, n) with n > 357,913,941 so n.checked_mul(12) overflows u32.
Common situations: A garbage/huge u32 year offset from malformed config or data, attempting to rewind a timestamp before epoch in backfill logic, and overflow unit tests.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- DateTime timestamp out of range for UnixNanos: {nanos}
- seconds {secs} is out of range for `u64` nanoseconds
- seconds {secs} is out of range for `u64` milliseconds
- milliseconds {millis} is out of range for `u64` nanoseconds
- microseconds {micros} is out of range for `u64` nanoseconds
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/d45a8ea736cbc90a.
Report an issue: GitHub.