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
- Inspect `exc.status` and `exc.headers` from the caught WSServerHandshakeError to see what the server actually returned.
- Verify the URL scheme matches the transport (`wss://` for TLS, `ws://` for plain).
- Confirm the server actually speaks WebSocket on that path (curl with `--include --http1.1 -H 'Connection: Upgrade' -H 'Upgrade: websocket'`).
- 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
- Verify the endpoint actually serves WebSocket (correct path + scheme).
- Ensure required auth/cookies are attached before ws_connect.
- Confirm proxy config forwards the Upgrade headers.
- Log `e.status` and `e.headers` to diagnose non-101 responses.
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
- Invalid challenge response
- Invalid window size
- Extension for deflate not supported{ext}
- Invalid upgrade header
- Invalid connection header
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/12f47db00eeee1fa.json.
Report an issue: GitHub.