DioxusLabs/dioxus · error
protocols is an invalid header value
Error message
protocols is an invalid header value
What it means
The fullstack WebSocket client builds its RFC 6455 handshake manually and writes Sec-WebSocket-Protocol via HeaderValue::from_str(protocols.join(", ")). from_str rejects any byte outside visible ASCII (control characters, newlines, tabs, and all non-ASCII UTF-8 such as emoji or accented letters), so one malformed subprotocol name makes the expect panic.
Source
Thrown at packages/fullstack/src/payloads/websocket.rs:1261
HeaderValue::from_static("upgrade"),
);
headers.insert(
reqwest::header::UPGRADE,
HeaderValue::from_static("websocket"),
);
headers.insert(
reqwest::header::SEC_WEBSOCKET_KEY,
HeaderValue::from_str(&nonce_value).expect("nonce is a invalid header value"),
);
headers.insert(
reqwest::header::SEC_WEBSOCKET_VERSION,
HeaderValue::from_static("13"),
);
if !protocols.is_empty() {
headers.insert(
reqwest::header::SEC_WEBSOCKET_PROTOCOL,
HeaderValue::from_str(&protocols.join(", "))
.expect("protocols is an invalid header value"),
);
}
Some(nonce_value)
}
Version::HTTP_2 => {
// TODO: Implement websocket upgrade for HTTP 2.
return Err(HandshakeError::UnsupportedHttpVersion(version).into());
}
_ => {
return Err(HandshakeError::UnsupportedHttpVersion(version).into());
}
};
// execute request
let response = client.execute(request).await?;
Ok(WebSocketResponse {View on GitHub (pinned to 393d190a80)
Solutions
- Restrict subprotocol names to RFC 6455 token characters: ASCII letters, digits, and - _ . + ~ ! $ & ' ( ) * + , ; = as appropriate tokens
- Sanitize/validate protocol strings before passing them to the WebSocket configuration
- URL-encode or hash any dynamic value embedded in a protocol name
Example fix
// before
ws.subprotocol(format!("auth-{token}")); // token contains non-ASCII -> panic
// after
ws.subprotocol(format!("auth-{}", simple_hash(&token))); // ASCII-only name Defensive patterns
Strategy: validation
Validate before calling
fn is_valid_subprotocol(p: &str) -> bool {
!p.is_empty()
&& p.bytes().all(|b| (0x21..=0x7e).contains(&b) && b != b',') // visible ASCII, no delimiter
&& !p.starts_with(' ')
}
assert!(protocols.iter().all(|p| is_valid_subprotocol(p))); Type guard
fn valid_subprotocols<'a>(ps: impl IntoIterator<Item = &'a str>) -> Option<String> {
let joined = ps.into_iter().collect::<Vec<_>>().join(", ");
joined.bytes().all(|b| (0x20..=0x7e).contains(&b)).then_some(joined)
} Prevention
- Use fixed ASCII identifiers for subprotocols; never embed raw user input
- Hash or base64url-encode dynamic values before using them as protocol names
- Validate protocol strings in one place at config-construction time
When it happens
Trigger: Configuring the WebSocket client with .subprotocol(...) entries containing non-ASCII or control characters — newlines, tabs, emoji, non-latin scripts — or programmatically composed protocol strings that embed unvalidated user input.
Common situations: Passing auth tokens, locale names, or free-form user strings as subprotocols; copy-pasting protocol names containing invisible unicode; joining protocols that already contain commas/whitespace.
Related errors
- Failed to parse index.html from public directory
- Failed to create new router after hot-patch!
- Lazy value is not initialized. Make sure to call `initialize
- Failed to serialize SSE event
- todo: convert_resize_data in dioxus-native. requires support
AI-assisted analysis of DioxusLabs/dioxus@393d190a80 (2026-08-16).
Data as JSON: /api/errors/25a82462c3d2d554.
Report an issue: GitHub.