n0-computer/iroh · error · InvalidBucketConfig

InvalidBucketConfig

InvalidBucketConfig

Error message

invalid bucket config

What it means

A rate-limit token bucket was constructed with parameters that can never work: non-positive capacity, non-positive bytes_per_second, a zero-millisecond refill period, or a computed refill amount that rounds down to zero. The constructor `new` validates these up front and returns InvalidBucketConfig.

Solutions

  1. Set max and bytes_per_second to positive values and refill_period to at least 1ms.
  2. Ensure bytes_per_second * refill_period_ms >= 1000 so the refill amount is > 0 (e.g. 1 KiB/s needs >= ~977ms period).
  3. Validate rate-limit values when loading configuration and reject 0/negatives early.
  4. If a very small rate is intended, lengthen refill_period rather than shrinking it.

Example fix

// before
RateBucket::new(now, 0, 1024, Duration::from_millis(0))?; // invalid
// after
RateBucket::new(now, 1024 * 1024, 1024, Duration::from_secs(1))?;
Defensive patterns

Strategy: validation

Validate before calling

fn valid_bucket_config(max: i64, bps: i64, period: std::time::Duration) -> bool {
    max > 0 && bps > 0 && period.as_millis() > 0 && (bps.saturating_mul(period.as_millis() as i64) / 1000) > 0
}

Type guard

fn checked_bucket(max: i64, bps: i64, period: std::time::Duration) -> Option<(i64, i64, std::time::Duration)> {
    valid_bucket_config(max, bps, period).then_some((max, bps, period))
}

Try / catch

match RateBucket::new(now, max, bps, period) {
    Err(e) => { log::error!("invalid rate-limit config: {:?}", e); return Err(ConfigError::RateLimit(e)); }
    Ok(b) => b,
}

Prevention

When it happens

Trigger: Calling RateBucket::new with max <= 0, bytes_per_second <= 0, refill_period < 1ms, or a combination where bytes_per_second * refill_period_ms / 1000 saturates to 0 (e.g. very small rate with a very short refill period).

Common situations: Relay rate-limit config loaded from files/env as 0 or negative defaults, unit conversions losing precision (bytes/sec to ms buckets), users setting refill_period below 1ms because tokio's timer is millisecond-resolution.

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 n0-computer/iroh@2b4de030ce (2026-09-08). Data as JSON: /api/errors/641bace2753ca59f. Report an issue: GitHub.

Appendix: source

Thrown at iroh-relay/src/server/streams.rs:405

}

impl Bucket {
    /// Creates a new bucket starting full at `max` tokens, refilled at
    /// `bytes_per_second` over `refill_period` intervals.
    ///
    /// # Errors
    ///
    /// Returns [`InvalidBucketConfig`] when `max`, `bytes_per_second`, or
    /// `refill_period` are non-positive, or when the configuration would refill
    /// less than one token per period.
    pub fn new(
        max: i64,
        bytes_per_second: i64,
        refill_period: time::Duration,
    ) -> Result<Self, InvalidBucketConfig> {
        // milliseconds is the tokio timer resolution
        let refill = bytes_per_second.saturating_mul(refill_period.as_millis() as i64) / 1000;
        ensure!(
            max > 0 && bytes_per_second > 0 && refill_period.as_millis() as u32 > 0 && refill > 0,
            InvalidBucketConfig {
                max,
                bytes_per_second,
                refill_period
            }
        );
        Ok(Self {
            fill: max,
            max,
            last_fill: time::Instant::now(),
            refill_period,
            refill,
        })
    }

    fn from_config(cfg: Option<ClientRateLimit>) -> Result<Option<Self>, InvalidBucketConfig> {
        match cfg {

View on GitHub (pinned to 2b4de030ce)