n0-computer/iroh · error · RelayUpgradeReqError
UnsupportedWebsocketVersion
UnsupportedWebsocketVersion
Error message
invalid header value for {SEC_WEBSOCKET_VERSION}: unsupported websocket version, only supporting {SUPPORTED_WEBSOCKET_VERSION} What it means
A relay WebSocket upgrade request carried a Sec-WebSocket-Version header that does not match the single version the relay server supports (13). The server rejects the upgrade before establishing the relay connection. This is thrown in handle_relay_ws_upgrade when version != SUPPORTED_WEBSOCKET_VERSION.
Solutions
- Send Sec-WebSocket-Version: 13 in the WS handshake headers (any standard modern WS client does this by default).
- Use the official iroh client/endpoint code to connect to the relay instead of a hand-rolled handshake.
- If behind a proxy, configure it to pass the Sec-WebSocket-Version header through unchanged.
- Check the client library version for known draft-version defaults and upgrade it.
Example fix
// before (hand-rolled handshake)
let req = Request::builder().uri(relay_url).header("Sec-WebSocket-Version", "8").body(...)?;
// after
let req = Request::builder().uri(relay_url).header("Sec-WebSocket-Version", "13").body(...)?; Defensive patterns
Strategy: validation
Validate before calling
const WS_VERSION: &str = "13";
fn is_supported_ws_version(headers: &[(String, String)]) -> bool {
headers.iter().any(|(k, v)| k.eq_ignore_ascii_case("sec-websocket-version") && v.trim() == WS_VERSION)
} Type guard
fn supported_version(v: &str) -> Option<&str> { (v == "13").then_some(v) } Try / catch
match res {
Err(RelayUpgradeReqError::UnsupportedWebsocketVersion) => respond_400("unsupported websocket version"),
other => other,
} Prevention
- Always let a mainstream WS client set the version header (it sends 13 by default).
- Never strip Sec-* headers in proxies.
- Pin client library versions and review their handshake defaults.
- Add an integration test against your relay endpoint.
When it happens
Trigger: Sending a WebSocket handshake to the iroh relay HTTP server with a Sec-WebSocket-Version header other than the supported value (e.g. '8' or '7'), or omitting/garbling the header so it fails the byte comparison against SUPPORTED_WEBSOCKET_VERSION.
Common situations: Custom or hand-rolled relay clients that implement the WS handshake themselves, proxies or middlewares that rewrite/drop the version header, older WS client libraries defaulting to draft versions (hixie-76 / hybi-08).
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- UnexpectedUpgradeStatus
- The relay is rate-limiting this endpoint; outbound relay…
- Invalid transport configuration
- ExceedsMaxPacketSize
- EmptyPacket
AI-assisted analysis of n0-computer/iroh@2b4de030ce (2026-09-08).
Data as JSON: /api/errors/c35b026f7ef6d4fc.
Report an issue: GitHub.
Appendix: source
Thrown at iroh-relay/src/server/http_server.rs:598
) -> Result<&HeaderValue, RelayUpgradeReqError> {
req.headers()
.get(&header)
.ok_or_else(|| e!(RelayUpgradeReqError::MissingHeader { header }))
}
let upgrade_header = expect_header(&req, UPGRADE)?;
ensure!(
upgrade_header == HeaderValue::from_static(WEBSOCKET_UPGRADE_PROTOCOL),
RelayUpgradeReqError::InvalidHeader {
header: UPGRADE,
details: format!("value must be {WEBSOCKET_UPGRADE_PROTOCOL}")
}
);
let key = expect_header(&req, SEC_WEBSOCKET_KEY)?.clone();
let version = expect_header(&req, SEC_WEBSOCKET_VERSION)?.clone();
ensure!(
version.as_bytes() == SUPPORTED_WEBSOCKET_VERSION.as_bytes(),
RelayUpgradeReqError::UnsupportedWebsocketVersion
);
let subprotocols = expect_header(&req, SEC_WEBSOCKET_PROTOCOL)?
.to_str()
.ok()
.ok_or_else(|| {
e!(RelayUpgradeReqError::InvalidHeader {
header: SEC_WEBSOCKET_PROTOCOL,
details: "header value is not ascii".to_string()
})
})?;
let protocol_version = subprotocols
.split(",")
.map(|s| s.trim())
.filter_map(ProtocolVersion::match_from_str)
.max()View on GitHub (pinned to 2b4de030ce)