nautechsystems/nautilus_trader · error

`step` exceeds i32 range for year arithmetic

Error message

`step` exceeds i32 range for year arithmetic

What it means

For Year bars, the step (i64) is narrowed to i32 for year arithmetic; if the value exceeds i32::MAX (or is negative), try_from fails and the expect panics. The function explicitly documents that `step` must be representable for the calendar arithmetic.

Source

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

                start_time =
                    subtract_n_months(start_time, 12).expect("Failed to subtract 12 months");
            }

            let months_step =
                u32::try_from(step).expect("`step` exceeds u32 range for month arithmetic");

            while start_time <= now {
                start_time =
                    add_n_months(start_time, months_step).expect("Failed to add months in loop");
            }

            start_time =
                subtract_n_months(start_time, months_step).expect("Failed to subtract months_step");
            start_time
        }
        BarAggregation::Year => {
            let step_i32 =
                i32::try_from(step).expect("`step` exceeds i32 range for year arithmetic");

            // 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)

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Fix the Year bar step to the intended small positive integer of years.
  2. Validate 0 < step <= i32::MAX before constructing the BarType.
  3. Reject such bar specs at configuration-parse time rather than at timer registration.
  4. If a huge period is genuinely needed, compose it via Month/Day aggregation instead.

Example fix

// before
BarType::from_spec(10_000_000_000, BarAggregation::Year)
// after
assert!(step > 0 && step <= i32::MAX as i64);
BarType::from_spec(step, BarAggregation::Year)
Defensive patterns

Strategy: validation

Validate before calling

fn validate_year_step(spec: &BarSpec) -> Result<(), String> {
    if spec.aggregation != BarAggregation::Year { return Ok(()); }
    let step = step_to_i64(spec.step);
    if step <= 0 { return Err("year step must be positive".into()); }
    if step > i32::MAX as i64 { return Err("year step exceeds i32 range".into()); }
    Ok(())
}

Type guard

fn valid_year_step(step: i64) -> bool { step > 0 && step <= i32::MAX as i64 }

Try / catch

let start = std::panic::catch_unwind(|| get_time_bar_start(now, &bar_type, origin))
    .map_err(|_| anyhow::anyhow!("invalid year step for {bar_type}"))?;

Prevention

When it happens

Trigger: get_time_bar_start (or start_timer_internal registering the timer) with BarAggregation::Year and a spec step > 2,147,483,647 years, or a negative step.

Common situations: Unit confusion when building the BarType spec (passing nanoseconds or seconds as the year step); programmatic spec generation without bounds checks; corrupted configuration files.

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/71c657f62a03aba2. Report an issue: GitHub.