nautechsystems/nautilus_trader · error
Failed to add months in loop
Error message
Failed to add months in loop
What it means
After stepping the Month anchor forward by months_step until it passes `now`, the function subtracts months_step once to land on the last aligned start. If add_n_months returns Err during the loop (timestamp pushed beyond jiff's supported range), the expect panics. This is effectively a timestamp-range overflow, not a normal business condition.
Source
Thrown at crates/model/src/data/bar.rs:278
.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");
// 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),View on GitHub (pinned to 18893faf8b)
Solutions
- Sanity-check the `now` Timestamp; it should be a realistic current time.
- Reduce the Month step to a sane value (e.g. < 1200 months) before constructing the BarType.
- If synthetic/backtesting time is used, keep it within jiff's supported year range.
- Report an issue if it reproduces with normal inputs.
Example fix
// before get_time_bar_start(now_ns_far_future, &month_bar_type, None) // after assert!(now.as_unix_nanos() > 0 && now.as_unix_nanos() < 4_102_444_800_000_000_000); // year 2100 get_time_bar_start(now, &month_bar_type, None)
Defensive patterns
Strategy: validation
Validate before calling
if now.as_unix_nanos() <= 0 || now.as_unix_nanos() >= 4_102_444_800_000_000_000 {
return Err("timestamp outside sane range (1970-2100)".into());
}
let start = get_time_bar_start(now, &bar_type, origin); Type guard
fn sane_timestamp(ts: Timestamp) -> bool {
ts.as_unix_nanos() > 0 && ts.as_unix_nanos() < 4_102_444_800_000_000_000
} Try / catch
let start = std::panic::catch_unwind(|| get_time_bar_start(now, &bar_type, origin))
.map_err(|_| anyhow::anyhow!("month bar start computation overflowed"))?; Prevention
- Sanity-check `now` (real clock or controlled backtest clock) before computing bar starts.
- Keep synthetic/backtest time within a bounded window.
- Cap month steps to practical magnitudes (< 1200).
When it happens
Trigger: get_time_bar_start with a Month bar where repeatedly adding months_step from the year anchor exceeds the maximum representable jiff timestamp — only reachable with an extreme `now`, huge months_step, or out-of-range origin offset.
Common situations: A `now` timestamp far in the future (bad clock, fake/synthetic time misconfigured); a very large month step near u32 bounds combined with an in-range `now`.
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 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/76100a054996071a.
Report an issue: GitHub.