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
- Check `exc.headers.get('Connection')` — it should contain `upgrade`.
- Ensure the server/proxy sends `Connection: upgrade` alongside `Upgrade: websocket`.
- Connect directly to the WS backend, bypassing any proxy/gateway, to confirm the backend itself upgrades correctly.
- 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
- Server/proxy must send `Connection: upgrade` together with `Upgrade: websocket`.
- Verify HTTP/1.1 is used end-to-end (HTTP/2 gateways can break WS upgrade).
- Test against the backend directly to isolate intermediary faults.
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
- Invalid upgrade header
- Invalid window size
- Extension for deflate not supported{ext}
- Invalid response status
- Invalid challenge response
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/148539fca645ee96.json.
Report an issue: GitHub.