aio-libs/aiohttp · error · WSServerHandshakeError

Invalid response status

Error message

Invalid response status

What it means

Raised as `WSServerHandshakeError` (client.py:1090-1099) during `ws_connect` when the server's response status code is not `101 Switching Protocols`. A successful WebSocket opening handshake must return 101; anything else (200, 301, 401, 403, 404, 500, etc.) means the upgrade was refused or short-circuited. The exception carries `request_info`, `history`, `status`, and `headers` for diagnosis.

Source

Thrown at aiohttp/client.py:1093

            )

        # send request
        resp = await self.request(
            method,
            url,
            params=params,
            headers=real_headers,
            read_until_eof=False,
            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(

View on GitHub (pinned to c0ef574e29)

Solutions

  1. Inspect `exc.status` and `exc.headers` from the caught WSServerHandshakeError to see what the server actually returned.
  2. Verify the URL scheme matches the transport (`wss://` for TLS, `ws://` for plain).
  3. Confirm the server actually speaks WebSocket on that path (curl with `--include --http1.1 -H 'Connection: Upgrade' -H 'Upgrade: websocket'`).
  4. Provide required auth/cookies/headers (`headers=`, `cookies=`) or fix the proxy's Upgrade forwarding.

Example fix

// before
await session.ws_connect('https://host/api')  # REST endpoint, returns 200
// after
await session.ws_connect('wss://host/ws')   # correct WS endpoint
Defensive patterns

Strategy: try-catch

Try / catch

from aiohttp import WSServerHandshakeError

try:
    ws = await session.ws_connect(url)
except WSServerHandshakeError as e:
    if e.status != 101:
        log.error('server returned %s, headers=%s', e.status, dict(e.headers))
        # check scheme/auth/proxy, then retry or fail
    raise

Prevention

When it happens

Trigger: Hitting an endpoint that isn't a WebSocket server (e.g., plain HTTP); server requires auth and returns 401; proxy/load-balancer returns 200 for an HTML error page; URL uses `http://` instead of `wss://`; server doesn't have WS enabled on that route.

Common situations: Wrong scheme (`http` vs `ws`, `https` vs `wss`); hitting a REST endpoint instead of the WS endpoint; TLS termination proxy stripping Upgrade headers; reverse proxy (nginx) not configured with `proxy_set_header Upgrade $http_upgrade`; auth/cookie missing so server returns 401/403.

Related errors


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