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
- 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).
- Reduce the time_bars_origin offset in the BarType specification so the anchor stays within range.
- If you legitimately need boundary-era dates, upgrade the calendar arithmetic to checked fallible handling or use a wider date library.
- 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
- Validate timestamps at system boundaries (clock sync, data ingestion) before bar aggregation.
- Keep time_bars_origin offsets small and validated in configuration.
- Test timer registration with boundary timestamps in CI.
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
- Failed to add months in loop
- Failed to subtract months_step
- UnixNanos overflow in from_seconds
- UnixNanos overflow in from_millis
- UnixNanos overflow in from_micros
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/046fb50ef0e7b090.
Report an issue: GitHub.