nautechsystems/nautilus_trader · error

Invalid interval_ns: {interval_ns} (must be non-zero)

Error message

Invalid interval_ns: {interval_ns} (must be non-zero)

What it means

RateLimit::new_checked validates its inputs before constructing a throttler. This error means interval_ns was 0, which would make the rate-limit window meaningless (division-by-zero / zero-window). The library refuses to construct a RateLimit with a non-zero limit but zero interval.

Source

Thrown at crates/common/src/throttler.rs:70

/// throttling entirely.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct RateLimit {
    limit: NonZeroUsize,
    interval_ns: NonZeroU64,
}

impl RateLimit {
    /// Creates a new [`RateLimit`] instance with correctness checking.
    ///
    /// # Errors
    ///
    /// Returns an error if `limit` or `interval_ns` is zero.
    pub fn new_checked(limit: usize, interval_ns: DurationNanos) -> anyhow::Result<Self> {
        let limit = NonZeroUsize::new(limit)
            .ok_or_else(|| anyhow::anyhow!("Invalid limit: {limit} (must be non-zero)"))?;
        let interval_ns = NonZeroU64::new(interval_ns.as_u64()).ok_or_else(|| {
            anyhow::anyhow!("Invalid interval_ns: {interval_ns} (must be non-zero)")
        })?;
        Ok(Self { limit, interval_ns })
    }

    /// Creates a new [`RateLimit`] instance.
    ///
    /// # Panics
    ///
    /// Panics if `limit` or `interval_ns` is zero.
    #[must_use]
    pub fn new(limit: usize, interval_ns: DurationNanos) -> Self {
        Self::new_checked(limit, interval_ns).expect(FAILED)
    }

    /// Maximum number of messages that can be sent within the interval.
    #[must_use]
    pub const fn limit(&self) -> usize {
        self.limit.get()

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Pass a positive interval_ns (e.g. DurationNanos for 1s = 1_000_000_000) to new_checked.
  2. Validate/parse the interval from config before construction and reject 0 early with a clear config error.
  3. If the interval is derived, check the derivation (e.g. 1_000_000_000 / rate) cannot produce 0 (rate of 0 or clamped).

Example fix

// before
let rl = RateLimit::new_checked(100, DurationNanos::default())?;
// after
let interval_ns = DurationNanos::from(1_000_000_000); // 1 second
let rl = RateLimit::new_checked(100, interval_ns)?;
Defensive patterns

Strategy: validation

Validate before calling

let interval_ns_u64 = interval_ns.as_u64();
if limit == 0 || interval_ns_u64 == 0 {
    return Err(format!("limit and interval_ns must be non-zero, got limit={limit}, interval_ns={interval_ns_u64}"));
}

Try / catch

match RateLimit::new_checked(limit, interval_ns) {
    Ok(rl) => rl,
    Err(e) => { log::error!("rate limit config invalid: {e}"); return Err(e); }
}

Prevention

When it happens

Trigger: Calling RateLimit::new_checked(limit, DurationNanos::from(0)) or otherwise passing a zero nanosecond interval while limit is non-zero.

Common situations: Config files where an interval field defaults to 0 or is parsed as an empty/placeholder value; computing interval_ns from a frequency or divisor that evaluates to zero; a config migration that dropped the interval value.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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