nautechsystems/nautilus_trader · error

year arithmetic underflow

Error message

year arithmetic underflow

What it means

When the origin-adjusted Jan-1 anchor is after `now`, the current year is decremented by step_i32 using checked_sub; if that would underflow i32 (only possible with a negative or huge step combined with boundary years), the expect panics. It guards the backward year walk.

Source

Thrown at crates/model/src/data/bar.rs:306

            // Reconstruct from Jan 1 + origin each time to avoid leap-day drift
            let year_start = |year: i32| {
                let year = i16::try_from(year).expect("year exceeds Jiff supported range");
                Offset::UTC
                    .to_timestamp(
                        Date::new(year, 1, 1)
                            .expect("valid year start date")
                            .at(0, 0, 0, 0),
                    )
                    .expect("valid UTC year start")
                    + origin_offset
            };

            let mut year = i32::from(Offset::UTC.to_datetime(now).year());
            if year_start(year) > now {
                year = year
                    .checked_sub(step_i32)
                    .expect("year arithmetic underflow");
            }

            loop {
                let next_year = year
                    .checked_add(step_i32)
                    .expect("year arithmetic overflow");

                if year_start(next_year) > now {
                    break;
                }
                year = next_year;
            }

            year_start(year)
        }
        _ => panic!(
            "Aggregation type {} not supported for time bars",
            spec.aggregation

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Use a sane positive Year step; validate step bounds before constructing the BarType.
  2. Ensure `now` yields a normal UTC year (e.g. within 1900-2200).
  3. Reject bar specs whose step magnitude makes year ± step exceed i32 at config validation.
  4. File an issue if reproducible with realistic inputs.

Example fix

// before
BarType::from_spec(2_147_483_647, BarAggregation::Year) // step near i32::MAX
// after
assert!(step > 0 && step <= 9_000);
BarType::from_spec(step, BarAggregation::Year)
Defensive patterns

Strategy: validation

Validate before calling

let step = step_to_i64(spec.step);
if step <= 0 || step > 9_000 {
    return Err(format!("year step {step} out of safe range"));
}

Type guard

fn safe_year_step(step: i64) -> bool { step > 0 && step <= 9_000 }

Try / catch

let start = std::panic::catch_unwind(|| get_time_bar_start(now, &bar_type, origin))
    .map_err(|_| anyhow::anyhow!("year arithmetic underflow"))?;

Prevention

When it happens

Trigger: get_time_bar_start with a Year bar where year.checked_sub(step_i32) underflows i32 — requires a near-i32::MIN result, i.e. an extreme step_i32 and an extreme input year, or a negative step that passed earlier checks.

Common situations: Crafted/adversarial bar specs with near-i32::MAX magnitude steps; timestamps near the calendar era boundaries in synthetic data.

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


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/fc3f44ce407cf4f2. Report an issue: GitHub.