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.
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 add months in loop
- Failed to subtract 12 months
- Invalid timestamp
- Invalid `UnixNanos` for 'end'
- Invalid `UnixNanos` for 'start'
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/af608b80bc4bf967.
Report an issue: GitHub.
Appendix: 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)