nautechsystems/nautilus_trader · error · TransportError
Connection rate limiter requires at least one connection rat
Error message
Connection rate limiter requires at least one connection rate key
What it means
The mirror-side validation of resolve_connection_rate_limit: a connection rate limiter without at least one key has nothing to look up in the limiter, which is a configuration mistake. The builder rejects it at construction with this InvalidInput TransportError rather than creating a rate limiter that would never be consulted.
Source
Thrown at crates/network/src/websocket/client.rs:2856
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)>,
rate_limiter: Option<Arc<RateLimiter<Ustr, MonotonicClock>>>,
) -> Result<Arc<RateLimiter<Ustr, MonotonicClock>>, TransportError> {
if let Some(rate_limiter) = rate_limiter {
if default_quota.is_some() || !keyed_quotas.is_empty() {
return Err(TransportError::Io(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"Cannot combine a shared rate limiter with quota configuration",View on GitHub (pinned to 18893faf8b)
Solutions
- Provide at least one connection rate key when configuring a rate limiter
- If rate limiting is not needed, omit the limiter entirely instead of passing one with no keys
- Make the config layer require key(s) whenever the limiter flag/section is present
Example fix
// before
builder.connection_rate_limiter(limiter) // keys empty
// after
builder
.connection_rate_limiter(limiter)
.connection_rate_keys(Arc::from([ustr("default")])) Defensive patterns
Strategy: validation
Validate before calling
if rate_limiter.is_some() && connection_rate_keys.is_empty() {
return Err(anyhow!("rate limiter set without connection rate keys"));
} Try / catch
match WebSocketClient::new(...) {
Err(e) if e.to_string().contains("requires at least one connection rate key") => {
// supply keys or drop the limiter, then rebuild
}
r => r?,
} Prevention
- Require key(s) in your config schema whenever the limiter section is present
- Avoid default limiter construction in shared builders when keys come from config
- Keep limiter and keys wiring in one code path so they cannot diverge
When it happens
Trigger: Building WebSocketClient with Some(rate_limiter) but an empty connection_rate_keys slice.
Common situations: Constructing the limiter unconditionally but only filling keys conditionally (e.g. keys loaded from config that was empty); a refactor that separated limiter creation from key wiring; default limiter in a shared builder helper with per-environment key lists.
Related errors
- Connection rate keys require a connection rate limiter
- Both `api_key` and `api_secret` must be provided together
- Not a subscription channel: {kind}
- Verified action requires the configured provider identities
- {field} must be non-negative, was {value}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/ede5be20639701e2.
Report an issue: GitHub.