nautechsystems/nautilus_trader · error

Failed to subtract 12 months

Error message

Failed to subtract 12 months

What it means

get_time_bar_start computes the aligned start of a Month-aggregated time bar. When the origin offset pushes the Jan-1 anchor into the future relative to `now`, it subtracts 12 months to step back one year; if that calendar operation fails (outside jiff's supported range), the expect panics with this message. It is an internal invariant: with a valid in-range timestamp this path should never fail.

Source

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

            }

            start_time
        }
        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");

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Check the `now` Timestamp passed to get_time_bar_start / the timer registration for out-of-range values; clamp to a realistic range (e.g. 1970-2100).
  2. Reduce the time_bars_origin offset in the BarType specification so the anchor stays within range.
  3. If you legitimately need boundary-era dates, upgrade the calendar arithmetic to checked fallible handling or use a wider date library.
  4. File an issue with the exact timestamp and bar_type if this reproduces with ordinary dates.

Example fix

// before
get_time_bar_start(now, &bar_type, origin)
// after
let clamped = now.clamp(MIN_TS, MAX_TS); // keep within sane range before computing bar start
get_time_bar_start(clamped, &bar_type, origin)
Defensive patterns

Strategy: validation

Validate before calling

let lo = Timestamp::from_unix_nanos(-62_167_219_200_000_000_000); // year 0
let hi = Timestamp::from_unix_nanos(253_402_300_799_000_000_000); // year 9999
if now < lo || now > hi { return Err("timestamp outside calendar-supported range"); }
let start = get_time_bar_start(now, &bar_type, origin);

Type guard

fn in_calendar_range(ts: Timestamp) -> bool {
    ts.as_unix_nanos() >= -62_167_219_200_000_000_000
        && ts.as_unix_nanos() <= 253_402_300_799_000_000_000
}

Try / catch

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

Prevention

When it happens

Trigger: Calling get_time_bar_start (directly or via start_timer_internal when registering a time bar aggregation timer) with a Month bar whose Jan-1 + origin anchor lands after `now` AND whose timestamp is near jiff's representable bounds (-9999..9999), so subtract_n_months(start_time, 12) returns Err.

Common situations: Feeding an extreme or corrupted Timestamp (negative nanoseconds near the era bounds, misconfigured clock, bad data backfill) into bar aggregation; a large time_bars_origin offset combined with a boundary-year timestamp; custom tooling that constructs Timestamps outside normal trading ranges.

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/046fb50ef0e7b090. Report an issue: GitHub.