aio-libs/aiohttp · error · WSServerHandshakeError
Invalid connection header
Error message
Invalid connection header
What it means
Raised as WSServerHandshakeError when resp._upgraded is False, i.e. the Connection header did not include 'upgrade' (case-insensitive). Even with status 101 and Upgrade: websocket, the server must also signal Connection: Upgrade to complete RFC 6455.
Solutions
- Inspect the caught WSServerHandshakeError .headers to confirm what Connection value was received.
- Fix the server/proxy to send 'Connection: Upgrade'.
- Bypass intermediaries that rewrite hop-by-hop headers, or use wss:// end-to-end.
Example fix
// before
# server returns 101, Upgrade: websocket, but Connection: close
await session.ws_connect('wss://x') # raises Invalid connection header
// after
# fix the server to send 'Connection: Upgrade' Defensive patterns
Strategy: try-catch
Validate before calling
# Server-side prevention: # response_headers['Connection'] = 'Upgrade' # response_headers['Upgrade'] = 'websocket' # No client-side validation avoids this; the server must comply.
Type guard
def connection_includes_upgrade(header_value: str) -> bool:
return 'upgrade' in [v.strip().lower() for v in (header_value or '').split(',')] Try / catch
from aiohttp import WSServerHandshakeError
try:
ws = await session.ws_connect(url)
except WSServerHandshakeError as e:
if e.message == 'Invalid connection header':
# check e.headers for Connection; fix server/proxy and retry
raise
raise Prevention
- Confirm the server/proxy preserves hop-by-hop 'Connection: Upgrade'.
- Avoid middleboxes that rewrite Connection headers.
- In tests, assert both Upgrade and Connection headers are echoed correctly.
When it happens
Trigger: Server replies 101, Upgrade: websocket, but Connection: close (or no Connection header). resp._upgraded is determined from the Connection header and is False.
Common situations: Reverse proxy that strips or rewrites the Connection header. Hand-rolled server that sets Upgrade but forgets Connection. Intermediaries (load balancers, CDNs) rewriting hop-by-hop headers.
Related errors
- Invalid upgrade 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/148539fca645ee96.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/client.py:1119
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 d041d4d0fd)