nautechsystems/nautilus_trader · error

days exceed signed duration range

Error message

days exceed signed duration range

What it means

After days × 24 succeeds, the hour count is converted to a SignedDuration via try_from_hours, which has its own narrower representable range. If hours fits i64 but exceeds the SignedDuration range, this error is thrown. Like the overflow sibling, it propagates through callers that may panic via unwrap.

Source

Thrown at crates/model/src/data/bar.rs:364

    debug_assert_ne!(period_ns, 0, "bar period must be non-zero");

    // Use div_euclid for floor division (rounds toward -inf, not zero)
    // so negative deltas (now before origin) yield the previous period boundary
    let num_periods = time_difference.as_nanos().div_euclid(period_ns);

    base_time + SignedDuration::from_nanos_i128(num_periods * period_ns)
}

fn duration_days(days: i64) -> SignedDuration {
    try_duration_days(days).unwrap_or_else(|e| panic!("{e}"))
}

fn try_duration_days(days: i64) -> anyhow::Result<SignedDuration> {
    let hours = days
        .checked_mul(24)
        .ok_or_else(|| anyhow::anyhow!("days overflow i64 hours"))?;
    SignedDuration::try_from_hours(hours)
        .ok_or_else(|| anyhow::anyhow!("days exceed signed duration range"))
}

fn try_time_interval(step: usize, aggregation: BarAggregation) -> anyhow::Result<SignedDuration> {
    let step_i64 = i64::try_from(step)
        .map_err(|_| invalid_interval_step(step, aggregation, "step exceeds i64 range"))?;

    let duration = match aggregation {
        BarAggregation::Millisecond => SignedDuration::from_millis(step_i64),
        BarAggregation::Second => SignedDuration::from_secs(step_i64),
        BarAggregation::Minute => SignedDuration::try_from_mins(step_i64).ok_or_else(|| {
            invalid_interval_step(step, aggregation, "step exceeds signed duration range")
        })?,
        BarAggregation::Hour => SignedDuration::try_from_hours(step_i64).ok_or_else(|| {
            invalid_interval_step(step, aggregation, "step exceeds signed duration range")
        })?,
        BarAggregation::Day => try_scaled_days(step, aggregation, step_i64, 1)?,
        BarAggregation::Week => try_scaled_days(step, aggregation, step_i64, 7)?,
        BarAggregation::Month => try_scaled_days(step, aggregation, step_i64, 30)?,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Clamp days to the SignedDuration-representable range before conversion
  2. Handle the error from try_duration_days instead of panicking via duration_days
  3. Validate configured bar intervals against known aggregation limits

Example fix

// before
let dur = duration_days(days);
// after
let dur = match try_duration_days(days) { Ok(d) => d, Err(e) => { warn!("{e}"); return Err(e); } };
Defensive patterns

Strategy: try-catch

Validate before calling

if days.abs() > (i64::MAX / 86_400_000_000) { bail!("days outside SignedDuration range"); }

Try / catch

let dur = try_duration_days(days).unwrap_or_else(|e| { warn!("{e}"); fallback_duration });

Prevention

When it happens

Trigger: Calling duration_days/try_scaled_days with days whose ×24 hour value is within i64 but outside SignedDuration::try_from_hours' accepted range (very large positive or negative day counts).

Common situations: Malformed or malicious interval configuration; tests probing duration limits; unbounded arithmetic on user-supplied aggregation steps.

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


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/2a3bdbd36ce09a19. Report an issue: GitHub.