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
- Ensure `now` and the resulting bar start stay well within jiff's supported range (roughly year -9999 to 9999).
- Reduce time_bars_origin magnitude in the bar type configuration.
- Clamp inputs to a safe window (e.g. 1900-2200) before calling.
- 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
- Reject negative/pre-epoch timestamps during data ingestion.
- Bound origin offsets in bar type configuration.
- Clamp timestamps to a safe window (1900-2200) in backtesting setups.
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
- Failed to subtract 12 months
- Failed to add months in loop
- 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/af608b80bc4bf967.
Report an issue: GitHub.