nautechsystems/nautilus_trader · error · TransportError

Connection rate keys require a connection rate limiter

Error message

Connection rate keys require a connection rate limiter

What it means

resolve_connection_rate_limit enforces that connection rate keys are only meaningful alongside a rate limiter: keys select which entries in the limiter are consulted. If keys are provided but rate_limiter is None, the configuration is incomplete and this InvalidInput TransportError is returned at client construction. Providing keys without a limiter would silently do nothing, so the builder rejects it instead.

Source

Thrown at crates/network/src/websocket/client.rs:2849

            IncomingHandler::Epoch(epoch_handler),
            ping_handler,
            rate_limiter,
            state_sink,
            connection_rate_limit,
            InitialConnectOptions {
                retry_policy: initial_connect_retry_policy,
                cancellation_token,
            },
        )
        .await
    }

    fn resolve_connection_rate_limit(
        rate_limiter: Option<Arc<RateLimiter<Ustr, MonotonicClock>>>,
        keys: Arc<[Ustr]>,
    ) -> Result<Option<ConnectionRateLimit>, TransportError> {
        if rate_limiter.is_none() && !keys.is_empty() {
            return Err(TransportError::Io(std::io::Error::new(
                std::io::ErrorKind::InvalidInput,
                "Connection rate keys require a connection rate limiter",
            )));
        }

        if rate_limiter.is_some() && keys.is_empty() {
            return Err(TransportError::Io(std::io::Error::new(
                std::io::ErrorKind::InvalidInput,
                "Connection rate limiter requires at least one connection rate key",
            )));
        }

        Ok(rate_limiter.map(|limiter| ConnectionRateLimit { limiter, keys }))
    }

    fn resolve_rate_limiter(
        default_quota: Option<Quota>,
        keyed_quotas: Vec<(String, Quota)>,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Supply a RateLimiter instance alongside the connection_rate_keys
  2. If rate limiting is not wanted, clear the connection_rate_keys list
  3. Make your config layer construct limiter and keys together as one unit

Example fix

// before
builder.connection_rate_keys(keys) // no limiter set
// after
builder
    .connection_rate_limiter(Arc::new(RateLimiter::new(clock)))
    .connection_rate_keys(keys)
Defensive patterns

Strategy: validation

Validate before calling

if rate_limiter.is_none() && !connection_rate_keys.is_empty() {
    return Err(anyhow!("connection_rate_keys set without a rate limiter"));
}

Try / catch

match WebSocketClient::new(...) {
    Err(e) if e.to_string().contains("require a connection rate limiter") => {
        // either supply a limiter or clear keys, then rebuild
    }
    r => r?,
}

Prevention

When it happens

Trigger: Building WebSocketClient with a non-empty connection_rate_keys Arc<[Ustr]> but no rate_limiter (Option<Arc<RateLimiter<Ustr, MonotonicClock>>> = None).

Common situations: Config layers that populate rate keys from config but construct the limiter only under a separate flag; disabling the limiter for dev environments while leaving keys set; copy-paste of key lists without the limiter setup.

Related errors


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