aio-libs/aiohttp · error · WSServerHandshakeError

Invalid response status

Error message

Invalid response status

What it means

Raised as WSServerHandshakeError when the WebSocket upgrade response status is not 101 Switching Protocols. The server did not complete the handshake; common causes are auth failures, hitting a non-WebSocket endpoint, or a reverse proxy that doesn't forward the Upgrade headers.

Solutions

  1. Verify the URL scheme and path point to the WS endpoint (often /ws or /socket).
  2. Supply required auth via headers= or cookies= and confirm via curl with -i first.
  3. Configure the reverse proxy to forward Upgrade and Connection headers.
  4. Catch WSServerHandshakeError and inspect .status / .headers to diagnose.

Example fix

// before
await session.ws_connect('https://example.com/api')  # not a WS endpoint -> raises
// after
async with session.ws_connect('wss://example.com/ws', headers={'Authorization': 'Bearer ...'}) as ws:
    ...
Defensive patterns

Strategy: try-catch

Validate before calling

async def safe_ws_connect(session, url, **kw):
    # preflight: confirm the endpoint speaks WebSocket via a HEAD/GET
    async with session.get(url, allow_redirects=False) as r:
        if r.status >= 400:
            raise RuntimeError(f'endpoint returned {r.status}; not a WS endpoint?')
    return await session.ws_connect(url, **kw)

Type guard

def looks_like_ws_url(url: str) -> bool:
    return url.startswith('ws://') or url.startswith('wss://')

Try / catch

from aiohttp import WSServerHandshakeError

try:
    ws = await session.ws_connect(url)
except WSServerHandshakeError as e:
    if e.message == 'Invalid response status':
        # auth/path/proxy issue; inspect e.status, e.headers
        raise RuntimeError(f'WS handshake failed with status {e.status}')
    raise

Prevention

When it happens

Trigger: Calling session.ws_connect against a URL that is a plain HTTP page, requires authentication (401/403), doesn't exist (404), or whose proxy stripped the Upgrade headers (200 with HTML). resp.status != 101.

Common situations: Wrong scheme (ws:// vs wss://). Endpoint path is a REST URL, not a WS endpoint. Bearer/basic auth required and not supplied. Nginx/Apache in front of the WS server without Upgrade/Connection forwarding. TLS termination that drops headers.

Related errors


AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11). Data as JSON: /api/errors/12f47db00eeee1fa. Report an issue: GitHub.

Appendix: source

Thrown at aiohttp/client.py:1101

            )

        # 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 d041d4d0fd)