hasura/graphql-engine · error · WebSocketError

Missing {SEC_WEBSOCKET_PROTOCOL} header

Error message

Missing {SEC_WEBSOCKET_PROTOCOL} header

What it means

The WebSocket upgrade request lacked the `Sec-WebSocket-Protocol` HTTP header, which is required because the graphql-ws server negotiates the `graphql-transport-ws` subprotocol. Without this header the server cannot confirm the client speaks the expected protocol and rejects the connection with `WebSocketError::MissingProtocolHeader`.

Source

Thrown at v3/crates/graphql/graphql-ws/src/websocket/mod.rs:114

                        });
                    // Set the WebSocket id response header
                    response
                        .headers_mut()
                        .insert(SEC_WEBSOCKET_ID, websocket_id.to_string().parse()?);
                    Ok(response)
                })
            },
        );

        result.unwrap_or_else(IntoResponse::into_response)
    }
}

/// Error types for WebSocket connections.
#[derive(Debug, thiserror::Error)]
pub enum WebSocketError {
    /// Error when the Sec-WebSocket-Protocol header is missing
    #[error("Missing {SEC_WEBSOCKET_PROTOCOL} header")]
    MissingProtocolHeader,

    /// Error when the header value cannot be converted to a string
    #[error("{SEC_WEBSOCKET_PROTOCOL} header: {0}")]
    InvalidHeaderValue(#[from] ToStrError),

    /// Error when the GraphQL WebSocket protocol is not included
    #[error("Expecting {} protocol", protocol::GRAPHQL_WS_PROTOCOL)]
    ExpectingGraphqlWsProtocol,

    /// Error when setting the WebSocket ID header value fails in response
    #[error("Unable to set {SEC_WEBSOCKET_ID} header value: {0}")]
    WebSocketIdInvalidHeaderValue(#[from] InvalidHeaderValue),
}

impl tracing_util::TraceableError for WebSocketError {
    fn visibility(&self) -> tracing_util::ErrorVisibility {
        match self {

View on GitHub (pinned to 724551b9ae)

Solutions

  1. Pass the subprotocol when connecting: `new WebSocket(url, 'graphql-transport-ws')`
  2. If behind a proxy, configure it to forward the Sec-WebSocket-Protocol header on upgrades
  3. Verify with a raw handshake dump (curl -H 'Sec-WebSocket-Protocol: graphql-transport-ws' ... or wireshark) that the header reaches the server
  4. Upgrade client libs that are known to drop the header

Example fix

// before
const ws = new WebSocket('wss://api.example.com/graphql');
// after
const ws = new WebSocket('wss://api.example.com/graphql','graphql-transport-ws');
Defensive patterns

Strategy: validation

Validate before calling

const ws = new WebSocket(url, 'graphql-transport-ws'); // always pass the protocol

Try / catch

catch (e) { if (String(e).includes('Missing') && String(e).includes('header')) { reconnect with subprotocol; } }

Prevention

When it happens

Trigger: Opening a WebSocket to the graphql endpoint without specifying a subprotocol — e.g. `new WebSocket(url)` with no protocols argument, or a raw client that omits the header. The server inspects the upgrade request's Sec-WebSocket-Protocol header and fails when absent.

Common situations: Using a generic WS client or curl-based test that omits subprotocols; a proxy (nginx/envoy) stripping the Sec-WebSocket-Protocol header; client library upgrade that stopped sending protocols; browser clients forgetting the second WebSocket constructor argument.

Related errors


AI-assisted analysis of hasura/graphql-engine@724551b9ae (2026-08-28). Data as JSON: /api/errors/f18bd4e3b33ff8be. Report an issue: GitHub.