n0-computer/iroh · warning

The relay is rate-limiting this endpoint; outbound relay…

Error message

The relay is rate-limiting this endpoint; outbound relay traffic is being throttled. Send less data over the relay, or, if you operate this relay, raise Limits::client_rx. Read more about rate limiting at https://docs.iroh.computer/relays/rate-limiting

What it means

This is a relay health-status error from iroh-relay. The relay server is throttling traffic received from this client endpoint because it exceeded the configured receive limits (Limits::client_rx). Once throttling starts, data sent through the relay is delayed/dropped, so outbound relay traffic toward peers slows down.

Solutions

  1. Reduce the volume/frequency of data sent over the relay; prefer direct (holepunched) connections so traffic bypasses the relay.
  2. If you operate the relay, raise the client_rx limit in the relay's Limits configuration.
  3. Check relay connectivity/latency and whether direct connection establishment is failing, forcing all traffic through the relay.
  4. Back off and retry after throttling; the status is sent once per connection when throttling first begins.

Example fix

// before: relay operator with default limits
let server = RelayServer::builder().bind(0.0.0.0:3340).await?;
// after: raise per-client receive limit
let server = RelayServer::builder()
    .bind(0.0.0.0:3340).await?
    .client_rx(10_000_000)  // bytes/sec, raised from default
    .spawn();
Defensive patterns

Strategy: retry

Validate before calling

// No pre-call check possible; monitor client health statuses instead.
fn handle_health(status: &ClientHealth) {
    if matches!(status, ClientHealth::RateLimited) {
        // enable backpressure: queue relayed sends, prefer direct connections
    }
}

Type guard

fn is_rate_limited(h: &ClientHealth) -> bool { matches!(h, ClientHealth::RateLimited) }

Try / catch

match result {
    Ok(v) => v,
    Err(e) if is_rate_limited_msg(&e) => schedule_backoff_and_retry(),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: A client connected to an iroh relay sends data faster than the relay's client_rx rate limit allows. The relay emits this ClientHealth::RateLimited status once per connection when it first begins throttling reads from that client.

Common situations: Chatty applications that push large amounts of relayed (non-direct) traffic; many endpoints sharing one relay with per-client limits; operators who set Limits::client_rx too low for their workload; dev/test setups where heavy traffic intentionally flows over the relay instead of via direct connections.

Related errors


AI-assisted analysis of n0-computer/iroh@2b4de030ce (2026-09-08). Data as JSON: /api/errors/c7a082ec1bd6fc6a. Report an issue: GitHub.

Appendix: source

Thrown at iroh-relay/src/protos/relay.rs:136

/// One-way message from server to client indicating issues with the relay connection.
#[derive(Debug, Clone, PartialEq, Eq, derive_more::Display)]
#[non_exhaustive]
pub enum Status {
    /// The connection is healthy and recovered from previous problems.
    #[display("The connection is healthy and has recovered from previous problems")]
    Healthy,
    /// Another endpoint connected with the same endpoint id. No more messages will be received.
    #[display(
        "Another endpoint connected with the same endpoint id. No more messages will be received."
    )]
    SameEndpointIdConnected,
    /// The relay is rate-limiting traffic received from this endpoint.
    ///
    /// Sent once per connection when the relay first throttles reading from the client.
    #[display(
        "The relay is rate-limiting this endpoint; outbound relay traffic is being throttled. \
        Send less data over the relay, or, if you operate this relay, raise Limits::client_rx. \
        Read more about rate limiting at https://docs.iroh.computer/relays/rate-limiting"
    )]
    RateLimited,
    /// Placeholder for backwards-compatibility for future new health status variants.
    #[display("Unsupported health message ({_0})")]
    Unknown(u8),
}

impl Status {
    #[cfg(feature = "server")]
    fn write_to<O: BufMut>(&self, mut dst: O) -> O {
        match self {
            Status::Healthy => dst.put_u8(0),
            Status::SameEndpointIdConnected => dst.put_u8(1),
            Status::RateLimited => dst.put_u8(2),
            Status::Unknown(discriminant) => dst.put_u8(*discriminant),
        }
        dst

View on GitHub (pinned to 2b4de030ce)