{"record":{"id":"9242dc8ee8d2223e","repo":"n0-computer/iroh","slug":"unexpectedupgradestatus","errorCode":"UnexpectedUpgradeStatus","errorMessage":"Unexpected status during upgrade: {code}","messagePattern":"Unexpected status during upgrade: (.+?)","errorType":"http","errorClass":"ConnectError","httpStatus":null,"severity":"error","filePath":"iroh-relay/src/client.rs","lineNumber":338,"sourceCode":"        if let Some(token) = self.auth_token.as_ref() {\n            let value = HeaderValue::from_str(&format!(\"Bearer {token}\"))\n                .map_err(|_| e!(ConnectError::InvalidAuthToken))?;\n            builder = builder\n                .add_header(AUTHORIZATION, value)\n                .expect(\"valid header name\");\n        }\n\n        if let Some(client_auth) = KeyMaterialClientAuth::new(&self.secret_key, &stream) {\n            debug!(\"Using TLS key export for relay client authentication\");\n            builder = builder\n                .add_header(CLIENT_AUTH_HEADER, client_auth.into_header_value())\n                .expect(\n                    \"impossible: CLIENT_AUTH_HEADER isn't a disallowed header value for websockets\",\n                );\n        }\n        let (conn, response) = builder.connect_on(stream).await.anyerr()?;\n\n        n0_error::ensure!(\n            response.status() == hyper::StatusCode::SWITCHING_PROTOCOLS,\n            ConnectError::UnexpectedUpgradeStatus {\n                code: response.status()\n            }\n        );\n\n        let protocol_version_str = response\n            .headers()\n            .get(SEC_WEBSOCKET_PROTOCOL)\n            .and_then(|s| s.to_str().ok());\n        let protocol_version = protocol_version_str\n            .and_then(ProtocolVersion::match_from_str)\n            .ok_or_else(|| {\n                e!(ConnectError::BadVersionHeader {\n                    server_version: protocol_version_str.map(ToOwned::to_owned)\n                })\n            })?;\n","sourceCodeStart":320,"sourceCodeEnd":356,"githubUrl":"https://github.com/n0-computer/iroh/blob/2b4de030ce5e0133f272871a76f0c685c63f552a/iroh-relay/src/client.rs#L320-L356","documentation":"ConnectError::UnexpectedUpgradeStatus from iroh-relay's websocket-based client connect. After issuing the HTTP upgrade request over the established stream, the client requires hyper's 101 SWITCHING_PROTOCOLS status; any other status (e.g. 4xx/5xx from an intermediary) fails the websocket upgrade and this error carries the actual status code.","triggerScenarios":"Calling connect() on the relay websocket client when response.status() != 101 — e.g. the relay or a proxy replies with 400/401/403/404/500, or an HTTP/1.0 proxy mangles the Upgrade request.","commonSituations":"Corporate proxies or load balancers stripping WebSocket Upgrade headers; relays requiring auth returning 401/403; wrong relay URL or TLS termination returning 404/502; relay running an incompatible protocol version.","solutions":["Inspect the status code in the error: 401/403 means auth is missing/invalid; 404 means wrong URL/path; 502/503 means a proxy or relay is down.","Verify the relay URL, scheme (wss vs ws), and that the server supports the websocket transport.","Bypass or correctly configure intermediate proxies to pass Upgrade/Connection headers.","Update the iroh-relay client/server so protocol versions match."],"exampleFix":"match relay_client.connect().await {\n    Ok(conn) => /* use conn */,\n    Err(ConnectError::UnexpectedUpgradeStatus { code }) if code == 401 => {\n        // refresh auth token then retry\n    }\n    Err(e) => return Err(e.into()),\n}","handlingStrategy":"try-catch","validationCode":"// Pre-flight: confirm the relay URL answers an HTTP request before upgrading\nlet probe = reqwest::get(relay_url.join(\"ping\")?).await?;\nensure!(probe.status().is_success(), \"relay unreachable: {}\", probe.status());","typeGuard":null,"tryCatchPattern":"match client.connect().await {\n    Err(e) if format!(\"{e}\").contains(\"UnexpectedUpgradeStatus\") => {\n        // log status, check proxy/auth, retry with backoff\n    }\n    other => other,\n}","preventionTips":["Verify relay URL, scheme (wss/ws), and reachability before connecting","Ensure proxies pass Upgrade/Connection headers for websocket traffic","Send auth headers (CLIENT_AUTH_HEADER) when the relay requires them","Match client/server iroh versions to avoid protocol mismatches"],"tags":["network","websocket","http","relay","upgrade"],"backgroundTag":"unexpected-http-status","analyzedSha":"2b4de030ce5e0133f272871a76f0c685c63f552a","analyzedAt":"2026-09-08T04:26:47.755Z","contentChangedAt":"2026-09-08T04:26:47.755Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}