aio-libs/aiohttp · error · WSServerHandshakeError
Invalid upgrade header
Error message
Invalid upgrade header
What it means
Raised as WSServerHandshakeError when the response UPGRADE header (case-insensitive) is not 'websocket'. The server returned 101 but did not confirm the WebSocket upgrade, so the protocol is not actually established.
Solutions
- Inspect resp.headers in the caught WSServerHandshakeError to see what was sent.
- Fix the server/proxy to respond with exactly 'Upgrade: websocket'.
- If you cannot change the server, do not use ws_connect against it; speak raw HTTP/1.1 or pick a compliant endpoint.
Example fix
// before
# server returns 'Upgrade: h2c' on a 101
await session.ws_connect('wss://x') # raises Invalid upgrade header
// after
# fix the server to send 'Upgrade: websocket' Defensive patterns
Strategy: try-catch
Validate before calling
async def preflight_upgrade(session, url):
# Verify the server's handshake behavior before relying on it.
async with session.get(url) as r:
return r.headers.get('Upgrade', '').lower() Type guard
def upgrade_is_websocket(header_value: str) -> bool:
return header_value.strip().lower() == 'websocket' Try / catch
from aiohttp import WSServerHandshakeError
try:
ws = await session.ws_connect(url)
except WSServerHandshakeError as e:
if e.message == 'Invalid upgrade header':
# server/proxy is non-RFC-compliant; cannot use ws_connect
raise
raise Prevention
- Ensure the server emits exactly 'Upgrade: websocket' on 101.
- Don't let intermediaries rewrite the Upgrade header.
- Validate handshakes in staging before production rollout.
When it happens
Trigger: Server replies 101 with Upgrade: h2c or Upgrade: HTTP/2 or omits/misspells the UPGRADE header. resp.headers.get('Upgrade','').lower() != 'websocket'.
Common situations: Reverse proxy or framework that auto-responds 101 for any Upgrade but writes a different token. Custom server returning Upgrade: WebSocket with extra tokens or different casing/value. Bug in a hand-rolled WS server.
Related errors
- Invalid connection header
- Extension for deflate not supported
- Handshake error
- Invalid challenge response
- Invalid response status
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/e467c089f5f61da8.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/client.py:1110
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(
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, "")View on GitHub (pinned to d041d4d0fd)