aio-libs/aiohttp · error · WSServerHandshakeError

Invalid connection header

Error message

Invalid connection header

What it means

Raised as `WSServerHandshakeError` (client.py:1110-1117) when `resp._upgraded` is False. `_upgraded` is set internally when the parser sees a `Connection: upgrade` header *and* the underlying transport was actually switched to a raw/passthrough protocol. So status=101 and `Upgrade: websocket` passed, but the connection was not promoted — typically because the `Connection` header is missing/incorrect, or the parser didn't see the upgrade token.

Source

Thrown at aiohttp/client.py:1111

                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, "")
            match = base64.b64encode(hashlib.sha1(sec_key + WS_KEY).digest()).decode()
            if r_key != match:
                raise WSServerHandshakeError(
                    resp.request_info,
                    resp.history,
                    message="Invalid challenge response",
                    status=resp.status,
                    headers=resp.headers,
                )

View on GitHub (pinned to c0ef574e29)

Solutions

  1. Check `exc.headers.get('Connection')` — it should contain `upgrade`.
  2. Ensure the server/proxy sends `Connection: upgrade` alongside `Upgrade: websocket`.
  3. Connect directly to the WS backend, bypassing any proxy/gateway, to confirm the backend itself upgrades correctly.
  4. Update or reconfigure the intermediary (e.g., nginx needs both `proxy_set_header Upgrade` and `proxy_set_header Connection "upgrade"`).

Example fix

// before
# nginx only sets Upgrade, not Connection
// after (nginx)
# 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:
    conn = e.headers.get('Connection', '<missing>')
    if 'connection header' in (e.message or '').lower():
        log.error('Connection header was: %s', conn)
    raise

Prevention

When it happens

Trigger: Server returned 101 + Upgrade: websocket but `Connection` header is `keep-alive` or absent; the connection was kept in HTTP mode by an intermediary that didn't fully hand off the socket.

Common situations: Proxy sets Upgrade but not Connection header; server framework that 101s without actually detaching the HTTP parser; intermediary (HTTP/2 gateway) that doesn't pass through the connection upgrade.

Related errors


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