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

  1. Send Sec-WebSocket-Version: 13 in the WS handshake headers (any standard modern WS client does this by default).
  2. Use the official iroh client/endpoint code to connect to the relay instead of a hand-rolled handshake.
  3. If behind a proxy, configure it to pass the Sec-WebSocket-Version header through unchanged.
  4. 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

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


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)