nautechsystems/nautilus_trader · error

Invalid bar interval

Error message

Invalid bar interval

What it means

`get_bar_interval_ns` (crates/model/src/data/bar.rs:203) converts the `SignedDuration` returned by `get_bar_interval` into a `DurationNanos` with `try_from(...).expect("Invalid bar interval")`. The conversion fails when the interval is negative or its total nanoseconds exceed what DurationNanos (i64 nanoseconds, ~292 years) can hold, causing this panic.

Source

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

            // Proxy for comparing bar lengths
            duration_days(step.checked_mul(30).expect("`step` overflows i64 days"))
        }
        BarAggregation::Year => {
            // Proxy for comparing bar lengths
            duration_days(step.checked_mul(365).expect("`step` overflows i64 days"))
        }
        _ => panic!("Aggregation not time based"),
    }
}

/// Returns the bar interval as [`DurationNanos`].
///
/// # Panics
///
/// Panics if the aggregation method of the given `bar_type` is not time based.
#[must_use]
pub fn get_bar_interval_ns(bar_type: &BarType) -> DurationNanos {
    DurationNanos::try_from(get_bar_interval(bar_type)).expect("Invalid bar interval")
}

/// Returns the time bar start as a timezone-aware `Timestamp`.
///
/// # Panics
///
/// Panics if computing the base civil date or datetime from `now` fails,
/// if `step` cannot be represented for the calendar arithmetic,
/// or if the aggregation type is unsupported.
#[must_use]
pub fn get_time_bar_start(
    now: Timestamp,
    bar_type: &BarType,
    time_bars_origin: Option<SignedDuration>,
) -> Timestamp {
    let spec = bar_type.spec();
    let step = step_to_i64(spec.step);
    let origin_offset = time_bars_origin.unwrap_or(SignedDuration::ZERO);

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Reduce the bar step/aggregation so the total interval is under ~292 years (i64 nanoseconds).
  2. If you need longer intervals, avoid get_bar_interval_ns and work with the SignedDuration from get_bar_interval directly.
  3. Validate the step at BarType construction so the resulting interval fits i64 nanoseconds.

Example fix

// before
let ns = get_bar_interval_ns(&bar_type); // panics for huge steps
// after
let interval = get_bar_interval(&bar_type);
assert!(interval.is_positive());
let ns = DurationNanos::try_from(interval).unwrap_or_else(|_| {
    // handle oversized interval explicitly
    ...
});
Defensive patterns

Strategy: validation

Validate before calling

// rust
fn interval_fits_nanos(bar_type: &BarType) -> bool {
    let d = get_bar_interval(bar_type);
    d.is_positive() && DurationNanos::try_from(d).is_ok()
}

Try / catch

// Prefer explicit handling over the internal expect:
let interval = get_bar_interval(&bar_type);
let ns = DurationNanos::try_from(interval)
    .map_err(|_| BarConfigError::IntervalTooLarge)?;

Prevention

When it happens

Trigger: Calling `get_bar_interval_ns` with a time-based `BarType` whose interval converts to a duration that cannot fit into i64 nanoseconds (e.g. enormous Day/Week/Month/Year steps), or otherwise an interval the try_from rejects.

Common situations: Aggregating with a very large step so the interval exceeds i64 nanoseconds; the test `test_time_bar_aggregator_accepts_interval_above_i64_nanos` shows larger intervals are tolerated elsewhere but not representable as DurationNanos here.

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/224b26199b3f1809. Report an issue: GitHub.