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
- Set max and bytes_per_second to positive values and refill_period to at least 1ms.
- Ensure bytes_per_second * refill_period_ms >= 1000 so the refill amount is > 0 (e.g. 1 KiB/s needs >= ~977ms period).
- Validate rate-limit values when loading configuration and reject 0/negatives early.
- 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
- Validate rate-limit values when loading config; reject 0 and negatives.
- Remember tokio timers have 1ms resolution; keep refill_period >= 1ms.
- Ensure bps * period_ms >= 1000 so refill > 0.
- Add property tests for boundary configs.
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
- The relay is rate-limiting this endpoint; outbound relay…
- Invalid transport configuration
- EmptyPacket
- RelayUrlParseError
- Only a single default address can be set per IP family
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)