nautechsystems/nautilus_trader · error
Failed to add {n} months to {datetime}
Error message
Failed to add {n} months to {datetime} What it means
add_n_months shifts a jiff Timestamp forward by n months via shift_months and, on failure, raises "Failed to add {n} months to {datetime}". The library throws it when the resulting date is invalid (day-of-month has no counterpart in the target month) or out of jiff's representable range.
Source
Thrown at crates/core/src/datetime.rs:568
/// Subtract `n` months from a Jiff [`Timestamp`].
///
/// # Errors
///
/// Returns an error if the resulting date would be invalid or out of range.
pub fn subtract_n_months(datetime: Timestamp, n: u32) -> anyhow::Result<Timestamp> {
shift_months(datetime, -i64::from(n))
.map_err(|_| anyhow::anyhow!("Failed to subtract {n} months from {datetime}"))
}
/// Add `n` months to a Jiff [`Timestamp`].
///
/// # Errors
///
/// Returns an error if the resulting date would be invalid or out of range.
pub fn add_n_months(datetime: Timestamp, n: u32) -> anyhow::Result<Timestamp> {
shift_months(datetime, i64::from(n))
.map_err(|_| anyhow::anyhow!("Failed to add {n} months to {datetime}"))
}
/// Subtract `n` months from a given UNIX nanoseconds timestamp.
///
/// # Errors
///
/// Returns an error if the resulting timestamp is out of range or invalid.
pub fn subtract_n_months_nanos(unix_nanos: UnixNanos, n: u32) -> anyhow::Result<UnixNanos> {
let datetime = unix_nanos.to_datetime_utc();
let result = subtract_n_months(datetime, n)?;
let timestamp = result.as_nanosecond();
let nanos =
u64::try_from(timestamp).map_err(|_| anyhow::anyhow!("Negative timestamp not allowed"))?;
Ok(UnixNanos::from(nanos))
}
/// Add `n` months to a given UNIX nanoseconds timestamp.View on GitHub (pinned to 18893faf8b)
Solutions
- Anchor timestamps to day <= 28 (or a month-end-aware rounding) before month arithmetic.
- Validate that day-of-month exists in the target month and clamp, e.g. Feb 31 -> Feb 29, before calling.
- Reduce or sanity-check n; unreasonably large month counts push the result out of range.
- Use jiff's arithmetic directly if you need the underlying cause rather than the flattened message.
Example fix
// before
let next = add_n_months(Timestamp::from_str("2024-01-31T00:00:00Z")?, 1)?; // Feb 31 invalid
// after
let next = add_n_months(Timestamp::from_str("2024-01-28T00:00:00Z")?, 1)?; // safe anchor day Defensive patterns
Strategy: validation
Validate before calling
// Rust: verify the target month can hold the day before adding
fn target_day_ok(day: i8, months_ahead: i64) -> bool {
// approximate: days 1-28 always exist in every month
(1..=28).contains(&day) || months_ahead % 12 != 1 // crude guard; clamp to 28 for safety
} Try / catch
let next = add_n_months(datetime, n)
.with_context(|| format!("adding {n} months to {datetime}; avoid month-end anchors like Jan 31"))?; Prevention
- Anchor timer/bar schedules on day <= 28 to keep month arithmetic total.
- Sanity-check n from configuration; unbounded month counts overflow jiff's range.
When it happens
Trigger: Calling add_n_months(ts, n) from a month-end anchor like Jan 30/31 (Jan 31 + 1 month -> Feb 31), or from a timestamp near year 9999 where the shift overflows jiff's range; also via start_timer_internal and get_time_bar_start with large n.
Common situations: Setting monthly timer intervals anchored on the last day of a 31-day month; expiry-roll logic adding 1/3/6/12 months to month-end contract dates; misconfigured timer month counts (n in the thousands).
Related errors
- Failed to subtract {n} months from {datetime}
- DateTime timestamp out of range for UnixNanos: {nanos}
- seconds must be finite, was {secs}
- seconds {secs} is out of range for `u64` nanoseconds
- seconds {secs} is out of range for `u64` milliseconds
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/19b40efca8284a16.
Report an issue: GitHub.