n0-computer/iroh · error · RelayUpgradeReqError
InvalidHeader
InvalidHeader
Error message
invalid header value for {header}: {details} What it means
During a relay websocket upgrade, a required HTTP header was present but its value was invalid. The server validates upgrade headers (e.g. the Upgrade header must equal "websocket") and returns RelayUpgradeReqError::InvalidHeader when the value does not match the expected static value.
Solutions
- Send Upgrade: websocket (exactly, case per HeaderValue::from_static comparison) on the websocket handshake request.
- Check intermediate proxies/load balancers for header rewriting; disable websocket header normalization or pass headers through untouched.
- Use a standard websocket client library to perform the handshake instead of a hand-rolled HTTP request.
- Ensure HTTP/1.1 is used for the upgrade; HTTP/2 requires extended CONNECT, which this endpoint does not accept.
Example fix
// before: custom request with wrong upgrade value
let req = Request::builder()
.header("Upgrade", "WebSocket/1.0")
.header("Connection", "keep-alive");
// after: standard websocket upgrade headers
let req = Request::builder()
.header("Upgrade", "websocket")
.header("Connection", "Upgrade")
.header("Sec-WebSocket-Key", base64_key)
.header("Sec-WebSocket-Version", "13"); Defensive patterns
Strategy: validation
Validate before calling
const REQUIRED_UPGRADE: &str = "websocket";
fn upgrade_header_ok(req: &http::Request<impl _>) -> bool {
req.headers()
.get(http::header::UPGRADE)
.and_then(|v| v.to_str().ok())
.map(|v| v.eq_ignore_ascii_case(REQUIRED_UPGRADE))
.unwrap_or(false)
} Try / catch
match handle_relay_ws_upgrade(req, tunnel_service).await {
Err(RelayUpgradeReqError::InvalidHeader { header, details }) => {
tracing::debug!("bad {header:?} in upgrade: {details}");
Response::builder().status(400).body("invalid upgrade headers")
}
other => other,
} Prevention
- Use a maintained websocket client library for the handshake
- Verify proxies preserve Upgrade and Connection headers verbatim
- Perform upgrades over HTTP/1.1, not HTTP/2
- Include Sec-WebSocket-Key and Sec-WebSocket-Version: 13 in every request
When it happens
Trigger: handle_relay_ws_upgrade checks a request header (here UPGRADE) whose value is not the required constant — e.g. Upgrade: h2c, HTTP/2.0, or a modified value, instead of "websocket".
Common situations: Proxies or gateways rewriting/dropping the Upgrade header, custom HTTP clients not setting the websocket upgrade correctly, using HTTP/2 (where the classic Upgrade handshake doesn't apply), or missing the Connection: Upgrade header pairing.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
AI-assisted analysis of n0-computer/iroh@2b4de030ce (2026-09-08).
Data as JSON: /api/errors/861add8fb7b4d3dc.
Report an issue: GitHub.
Appendix: source
Thrown at iroh-relay/src/server/http_server.rs:587
res
}
/// Upgrades the HTTP connection to the relay protocol, runs relay client.
fn handle_relay_ws_upgrade(
&self,
mut req: Request<Incoming>,
) -> Result<Response<BytesBody>, RelayUpgradeReqError> {
fn expect_header(
req: &Request<Incoming>,
header: http::HeaderName,
) -> 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()View on GitHub (pinned to 2b4de030ce)