aio-libs/aiohttp · error · WSServerHandshakeError

Invalid upgrade header

Error message

Invalid upgrade header

What it means

Raised as `WSServerHandshakeError` (client.py:1101-1108) when the response to a WS upgrade request has an `Upgrade` header whose value (case-insensitive) is not exactly `websocket`. RFC 6455 requires the server to echo `Upgrade: websocket`; a mismatch means the server did not honour the upgrade (e.g., returned a normal HTTP response). Status was 101 but the Upgrade header is wrong/absent.

Source

Thrown at aiohttp/client.py:1102

            proxy=proxy,
            ssl=ssl,
            server_hostname=server_hostname,
            proxy_headers=proxy_headers,
        )

        try:
            # check handshake
            if resp.status != 101:
                raise WSServerHandshakeError(
                    resp.request_info,
                    resp.history,
                    message="Invalid response status",
                    status=resp.status,
                    headers=resp.headers,
                )

            if resp.headers.get(hdrs.UPGRADE, "").lower() != "websocket":
                raise WSServerHandshakeError(
                    resp.request_info,
                    resp.history,
                    message="Invalid upgrade header",
                    status=resp.status,
                    headers=resp.headers,
                )

            if not resp._upgraded:
                raise WSServerHandshakeError(
                    resp.request_info,
                    resp.history,
                    message="Invalid connection header",
                    status=resp.status,
                    headers=resp.headers,
                )

            # key calculation
            r_key = resp.headers.get(hdrs.SEC_WEBSOCKET_ACCEPT, "")

View on GitHub (pinned to c0ef574e29)

Solutions

  1. Inspect `exc.headers` to see the actual Upgrade header value.
  2. Fix the server/proxy to echo `Upgrade: websocket` on 101 responses.
  3. Bypass intermediaries (connect directly to the WS server) to isolate where the header is dropped.
  4. If the server genuinely doesn't support WS, switch to a real WS endpoint.

Example fix

// before
# nginx missing: proxy_set_header Upgrade $http_upgrade;
await session.ws_connect('wss://host/ws')
// after (server-side nginx fix)
# proxy_http_version 1.1;
# proxy_set_header Upgrade $http_upgrade;
# proxy_set_header Connection "upgrade";
Defensive patterns

Strategy: try-catch

Try / catch

from aiohttp import WSServerHandshakeError

try:
    ws = await session.ws_connect(url)
except WSServerHandshakeError as e:
    upg = e.headers.get('Upgrade', '<missing>')
    if 'invalid upgrade header' in (e.message or '').lower():
        log.error('Upgrade header was: %s', upg)
    raise

Prevention

When it happens

Trigger: Server returned 101 but with `Upgrade: h2c` or no Upgrade header; intermediary rewrote the Upgrade header; buggy server that sends 101 for non-WS reasons.

Common situations: Reverse proxy or CDN that returns 101 but strips/alters the Upgrade header; load balancer doing protocol negotiation; misconfigured server framework that doesn't fully implement RFC 6455.

Related errors


AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04). Data as JSON: /data/errors/e467c089f5f61da8.json. Report an issue: GitHub.