aio-libs/aiohttp · error · WSServerHandshakeError
Invalid challenge response
Error message
Invalid challenge response
What it means
Raised as `WSServerHandshakeError` (client.py:1119-1129) when the server's `Sec-WebSocket-Accept` header doesn't equal `base64(sha1(sec_key + GUID))`. The client generates a random `sec_key`, sends it in `Sec-WebSocket-Key`, and the server must echo back the SHA-1 of that key concatenated with the RFC 6455 GUID (`258EAFA5-E914-47DA-95CA-C5AB0DC85B11`). A mismatch means the server didn't follow the handshake proof — a sign of a broken/non-compliant server or an intermediary rewriting headers.
Source
Thrown at aiohttp/client.py:1123
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,
)
# websocket protocol
protocol = None
if protocols and hdrs.SEC_WEBSOCKET_PROTOCOL in resp.headers:
resp_protocols = [
proto.strip()
for proto in resp.headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(",")
]
for proto in resp_protocols:
if proto in protocols:
protocol = protoView on GitHub (pinned to c0ef574e29)
Solutions
- Verify the server implements RFC 6455 §4.2.2 §5 (SHA-1 of `sec_key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"`, base64-encoded).
- Compare the client's sent `Sec-WebSocket-Key` (in `exc.request_info.headers`) against what the server received — intermediaries sometimes corrupt it.
- Use a known-compliant WS server (autobahn, nginx, a real framework) to isolate the fault.
- If testing, ensure your mock computes the Accept correctly rather than returning a placeholder.
Example fix
// before
# custom server echoes key verbatim in Sec-WebSocket-Accept
// after (server-side, Python)
import hashlib, base64
accept = base64.b64encode(
hashlib.sha1(sec_key.encode() + b'258EAFA5-E914-47DA-95CA-C5AB0DC85B11').digest()
).decode() Defensive patterns
Strategy: try-catch
Try / catch
from aiohttp import WSServerHandshakeError
try:
ws = await session.ws_connect(url)
except WSServerHandshakeError as e:
if 'challenge response' in (e.message or '').lower():
log.error('Sec-WebSocket-Accept mismatch; server likely non-compliant')
# verify server implements RFC 6455 SHA-1+GUID proof
raise Prevention
- Use an RFC 6455-compliant server (autobahn testsuite to verify).
- Inspect whether intermediaries rewrite Sec-WebSocket-Key.
- For mocks, compute Accept = base64(sha1(key + GUID)).
When it happens
Trigger: Server returns 101 with all the right headers but computes the Accept value incorrectly (wrong GUID, wrong hash, or echoes the Key verbatim). Some custom/broken WS servers or test mocks do this.
Common situations: Hand-rolled WS server with a buggy Accept computation; mock server that doesn't implement the handshake proof; intermediary that rewrites the Sec-WebSocket-Key and desyncs the proof; server echoing Key instead of hashing.
Related errors
- Invalid response status
- 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/fce7217dcde46ca0.json.
Report an issue: GitHub.