nautechsystems/nautilus_trader · error

Failed to subtract months_step

Error message

Failed to subtract months_step

What it means

The final subtraction of months_step to return the aligned bar start fails (subtract_n_months returns Err) and the expect panics. After a successful forward loop this should not normally fail, but an origin offset near the lower boundary of jiff's range can push the subtract below the representable minimum.

Source

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

                )
                .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
                    .to_timestamp(
                        Date::new(year, 1, 1)
                            .expect("valid year start date")
                            .at(0, 0, 0, 0),
                    )
                    .expect("valid UTC year start")
                    + origin_offset
            };

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure `now` and the resulting bar start stay well within jiff's supported range (roughly year -9999 to 9999).
  2. Reduce time_bars_origin magnitude in the bar type configuration.
  3. Clamp inputs to a safe window (e.g. 1900-2200) before calling.
  4. File an issue with reproducing inputs if normal dates trigger it.

Example fix

// before
let start = get_time_bar_start(Timestamp::from_unix_nanos(-9_000_000_000_000_000_000), &bar_type, origin);
// after
let clamped = Timestamp::from_unix_nanos(now_ns.max(0));
let start = get_time_bar_start(clamped, &bar_type, origin);
Defensive patterns

Strategy: validation

Validate before calling

if now.as_unix_nanos() < 0 {
    return Err("pre-epoch timestamps unsupported for month bars".into());
}
let start = get_time_bar_start(now, &bar_type, origin);

Type guard

fn post_epoch(ts: Timestamp) -> bool { ts.as_unix_nanos() >= 0 }

Try / catch

let start = std::panic::catch_unwind(|| get_time_bar_start(now, &bar_type, origin))
    .map_err(|_| anyhow::anyhow!("month bar start subtraction underflowed"))?;

Prevention

When it happens

Trigger: get_time_bar_start with a Month bar where the loop result sits exactly one months_step above the minimum representable timestamp — e.g. `now` near the epoch lower bound with a large months_step or large negative origin offset.

Common situations: Negative/before-1970 timestamps in backtests or data imports; extremely large origin offset in the bar spec; corrupted timestamp 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/af608b80bc4bf967. Report an issue: GitHub.