nautechsystems/nautilus_trader · error

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

Error message

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

What it means

Raised by `Throttler::new_checked` when the `limit` parameter is zero. The throttler requires a strictly positive capacity, so the value is converted to `NonZeroUsize` and zero is rejected up front with this message (the sibling check rejects a zero `interval_ns`).

Source

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

/// The non-zero field types make a degenerate rate limit unrepresentable: a zero `limit`
/// underflows the throttler's `limit - 1` indexing, and a zero `interval_ns` disables
/// 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]

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Pass a positive `limit` (at least 1) to `Throttler::new_checked`.
  2. Check where the limit value originates (config/env) and validate it is > 0 before constructing the throttler.
  3. Fix computations that can truncate to zero (use floating point or ceil before casting to usize).
  4. Also ensure `interval_ns` is non-zero, since the constructor rejects that in the same call.

Example fix

// before
let limit = config.get("limit").unwrap_or(0);
let throttler = Throttler::new_checked(limit, interval_ns)?;

// after
let limit = config.get("limit").copied().filter(|&l| l > 0)
    .ok_or_else(|| anyhow::anyhow!("config 'limit' must be a positive integer"))?;
let throttler = Throttler::new_checked(limit, interval_ns)?;
Defensive patterns

Strategy: validation

Validate before calling

// Rust, before construction
assert!(limit > 0, "throttler limit must be non-zero");
assert!(interval_ns.as_u64() > 0, "throttler interval_ns must be non-zero");

Type guard

// Use NonZeroUsize/NonZeroU64 at the config boundary so zero is unrepresentable:
fn valid_throttle_config(cfg: &ThrottleConfig) -> bool {
    cfg.limit > 0 && cfg.interval_ns.as_u64() > 0
}

Try / catch

let throttler = Throttler::new_checked(limit, interval_ns)
    .context("invalid throttler configuration")?;

Prevention

When it happens

Trigger: Constructing a `Throttler` via `new_checked(0, interval_ns)` (crates/common/src/throttler.rs:68) — passing a limit computed at runtime that evaluated to 0 (e.g. from an empty config, a division that rounded to zero, or an unset option defaulting to 0).

Common situations: Config-driven throttler setup where the `limit` key is missing/0 in a YAML/JSON config, computing requests-per-second incorrectly (e.g. `secs as usize` truncation), or copy-pasted constructor calls with placeholder zeros.

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/9a503c06683fd44e. Report an issue: GitHub.