seanmonstar/warp · error · MissingConnectionUpgrade
MissingConnectionUpgrade
Error message
MissingConnectionUpgrade
What it means
warp::ws() (src/filters/ws.rs:50) requires the request to include a Connection header whose value contains "upgrade"; otherwise it rejects with MissingConnectionUpgrade. WebSocket handshakes per RFC 6455 must carry Connection: Upgrade alongside Upgrade: websocket, so a request lacking this header cannot begin a WS session.
Solutions
- Connect using a real WebSocket client (browser WebSocket API, tokio-tungstenite, etc.) rather than plain HTTP — it sets Connection: Upgrade and Upgrade: websocket automatically
- Configure reverse proxies to forward Upgrade and Connection headers (e.g. nginx: proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade")
- Ensure the request uses GET, since ws() composes warp::get()
- Recover the rejection and return a clear 426 Upgrade Required response for misdirected plain-HTTP requests
Example fix
// before (nginx)
location /ws {
proxy_pass http://backend;
}
// -> Connection header stripped -> MissingConnectionUpgrade
// after (nginx)
location /ws {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
} Defensive patterns
Strategy: validation
Validate before calling
// client-side: only open WS endpoints with a WebSocket client that sends upgrade headers
const isWsUrl = url.startsWith('ws://') || url.startsWith('wss://');
if (!isWsUrl) throw new Error(`use a WebSocket client for ${url}, not plain HTTP`); Type guard
function isWebSocketReady(req) {
return req.method === 'GET' &&
/upgrade/i.test(req.headers.connection || '') &&
/websocket/i.test(req.headers.upgrade || '');
} Try / catch
let resp = ws_routes.recover(|rej: warp::Rejection| async move {
if !rej.is_not_found() && rej.find::<warp::ws::MissingConnectionUpgrade>().is_some() {
Ok(warp::reply::with_status("Upgrade Required", warp::http::StatusCode::UPGRADE_REQUIRED))
} else { Err(rej) }
}); Prevention
- Use proper WebSocket client libraries, never fetch/axios, for ws:// endpoints
- Verify proxy forwarding of Upgrade and Connection headers (proxy_http_version 1.1 in nginx)
- Smoke-test the WS handshake in integration tests (e.g. warp::test::ws())
- Return 426 Upgrade Required in a recover handler so misconfigured clients get a clear signal
When it happens
Trigger: A request to a warp::ws() route that omits the Connection: upgrade header — typically a plain HTTP request, a client library not setting the header, or an intermediary proxy stripping hop-by-hop Connection headers. Also triggered if the request is not a GET (ws() composes warp::get()).
Common situations: Testing the WS endpoint with curl or Postman without WS mode; reverse proxies (older nginx/HAProxy configs) dropping the Connection/Upgrade headers; misconfigured clients hitting the WS route with ordinary fetch/axios instead of a WebSocket client; missing proxy_read_headers / Upgrade forwarding settings.
Related errors
AI-assisted analysis of seanmonstar/warp@ff34d7213e (2026-09-09).
Data as JSON: /api/errors/000997b3b10d949f.
Report an issue: GitHub.
Appendix: source
Thrown at src/filters/ws.rs:50
/// - Header `connection` must be `upgrade`
/// - Header `upgrade` must be `websocket`
/// - Header `sec-websocket-version` must be `13`
/// - Header `sec-websocket-key` must be set.
///
/// If the filters are met, yields a `Ws`. Calling `Ws::on_upgrade` will
/// return a reply with:
///
/// - Status of `101 Switching Protocols`
/// - Header `connection: upgrade`
/// - Header `upgrade: websocket`
/// - Header `sec-websocket-accept` with the hash value of the received key.
pub fn ws() -> impl Filter<Extract = One<Ws>, Error = Rejection> + Copy {
let connection_has_upgrade = header::header2()
.and_then(|conn: ::headers::Connection| {
if conn.contains("upgrade") {
future::ok(())
} else {
future::err(crate::reject::known(MissingConnectionUpgrade))
}
})
.untuple_one();
crate::get()
.and(connection_has_upgrade)
.and(header::exact_ignore_case("upgrade", "websocket"))
.and(header::exact("sec-websocket-version", "13"))
//.and(header::exact2(Upgrade::websocket()))
//.and(header::exact2(SecWebsocketVersion::V13))
.and(header::header2::<SecWebsocketKey>())
.and(on_upgrade())
.map(
move |key: SecWebsocketKey, on_upgrade: Option<OnUpgrade>| Ws {
config: None,
key,
on_upgrade,
},View on GitHub (pinned to ff34d7213e)