aio-libs/aiohttp · error · HTTPBadRequest
Unsupported version
Error message
Unsupported version: {version} What it means
_handshake() checks the Sec-WebSocket-Version header against the supported set {13, 8, 7} (RFC 6455 and its two drafts). Any other value (missing, empty, '12', etc.) returns HTTP 400. Version 13 is the standard (RFC 6455); 8 and 7 are legacy drafts kept for backward compatibility.
Solutions
- Ensure the client sends 'Sec-WebSocket-Version: 13' (all modern browsers and the aiohttp client do this by default).
- Upgrade client libraries to one supporting RFC 6455.
- If supporting legacy drafts is unnecessary, treat this 400 as expected behaviour for malformed clients.
Example fix
// before — hand-built request missing version
// headers: {Upgrade: websocket, Connection: Upgrade, ...}
// after
// headers: {Upgrade: websocket, Connection: Upgrade, 'Sec-WebSocket-Version': '13', 'Sec-WebSocket-Key': '<base64 16 bytes>'} Defensive patterns
Strategy: validation
Validate before calling
SUPPORTED_WS_VERSIONS = {'13', '8', '7'}
def is_supported_ws_version(request) -> bool:
return request.headers.get('Sec-WebSocket-Version', '') in SUPPORTED_WS_VERSIONS
if not is_supported_ws_version(request):
return web.Response(status=400, text='unsupported WS version') Type guard
def has_valid_ws_version(request) -> bool:
return request.headers.get('Sec-WebSocket-Version', '') == '13' Try / catch
ws = web.WebSocketResponse()
try:
await ws.prepare(request)
except web.HTTPBadRequest as e:
if 'Unsupported version' in (e.text or ''):
log.info('client sent unsupported WS version, rejecting')
return Prevention
- Use modern clients that send Sec-WebSocket-Version: 13 (RFC 6455).
- Don't try to support pre-RFC draft versions unless you have a known legacy client.
- Log rejected handshakes at info level — most are probes/scanners.
When it happens
Trigger: A client sends an outdated draft version (e.g. '6' or 'hixie-76'); the header is missing entirely (defaults to ''); a malformed/forged request with a non-numeric value; an extremely old browser library.
Common situations: Legacy client libraries from the draft era; misbehaving bots/scanners sending probe requests; intermediaries that strip the version header; tests with hand-built handshake requests that forget the version.
Related errors
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/c0094f0d3d238708.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/web_ws.py:311
]
for proto in req_protocols:
if proto in self._protocols:
protocol = proto
break
else:
# No overlap found: Return no protocol as per spec
ws_logger.warning(
"%s: Client protocols %r don’t overlap server-known ones %r",
request.remote,
req_protocols,
self._protocols,
)
# check supported version
version = headers.get(hdrs.SEC_WEBSOCKET_VERSION, "")
if version not in ("13", "8", "7"):
raise HTTPBadRequest(text=f"Unsupported version: {version}")
# check client handshake for validity
key = headers.get(hdrs.SEC_WEBSOCKET_KEY)
try:
if not key or len(base64.b64decode(key)) != 16:
raise HTTPBadRequest(text=f"Handshake error: {key!r}")
except binascii.Error:
raise HTTPBadRequest(text=f"Handshake error: {key!r}") from None
accept_val = base64.b64encode(
hashlib.sha1(key.encode() + WS_KEY).digest()
).decode()
response_headers = CIMultiDict(
{
hdrs.UPGRADE: "websocket",
hdrs.CONNECTION: "upgrade",
hdrs.SEC_WEBSOCKET_ACCEPT: accept_val,
}View on GitHub (pinned to d041d4d0fd)