nautechsystems/nautilus_trader · error

`step` exceeds u32 range for month arithmetic

Error message

`step` exceeds u32 range for month arithmetic

What it means

For Month bars, the aggregation step (an i64) is converted to u32 because add_n_months/subtract_n_months take u32 month counts. If the bar spec's step is negative or exceeds u32::MAX, try_from fails and the expect panics. The function documents this: `step` must be representable for the calendar arithmetic.

Source

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

        BarAggregation::Month => {
            // Set to the first day of the year
            let now_civil = Offset::UTC.to_datetime(now);
            let mut start_time = Offset::UTC
                .to_timestamp(
                    Date::new(now_civil.year(), 1, 1)
                        .expect("valid year start date")
                        .at(0, 0, 0, 0),
                )
                .expect("valid UTC year start");
            start_time += origin_offset;

            if now < start_time {
                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

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Correct the bar spec step to the intended number of months (a small positive integer).
  2. Validate step <= u32::MAX before constructing/registering the Month bar type.
  3. Guard the call: check spec.aggregation and spec.step bounds before calling get_time_bar_start.
  4. Use a Year aggregation instead if the intended period is many months.

Example fix

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

Strategy: validation

Validate before calling

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

Type guard

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

Try / catch

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

Prevention

When it happens

Trigger: Registering or computing a time bar with BarAggregation::Month and a step value > 4,294,967,295 months (e.g. step_to_i64 of a huge spec step) or a negative step reaching this branch.

Common situations: Typo or unit mistake in a bar spec (e.g. passing nanoseconds as the month step); programmatically generated BarType specs with unchecked step values; adversarial or corrupted configuration where the step field holds a raw byte value.

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/60fb0307922ef59d. Report an issue: GitHub.