aio-libs/aiohttp · error · WSServerHandshakeError
Invalid response status
Error message
Invalid response status
What it means
Raised as WSServerHandshakeError when the WebSocket upgrade response status is not 101 Switching Protocols. The server did not complete the handshake; common causes are auth failures, hitting a non-WebSocket endpoint, or a reverse proxy that doesn't forward the Upgrade headers.
Solutions
- Verify the URL scheme and path point to the WS endpoint (often /ws or /socket).
- Supply required auth via headers= or cookies= and confirm via curl with -i first.
- Configure the reverse proxy to forward Upgrade and Connection headers.
- Catch WSServerHandshakeError and inspect .status / .headers to diagnose.
Example fix
// before
await session.ws_connect('https://example.com/api') # not a WS endpoint -> raises
// after
async with session.ws_connect('wss://example.com/ws', headers={'Authorization': 'Bearer ...'}) as ws:
... Defensive patterns
Strategy: try-catch
Validate before calling
async def safe_ws_connect(session, url, **kw):
# preflight: confirm the endpoint speaks WebSocket via a HEAD/GET
async with session.get(url, allow_redirects=False) as r:
if r.status >= 400:
raise RuntimeError(f'endpoint returned {r.status}; not a WS endpoint?')
return await session.ws_connect(url, **kw) Type guard
def looks_like_ws_url(url: str) -> bool:
return url.startswith('ws://') or url.startswith('wss://') Try / catch
from aiohttp import WSServerHandshakeError
try:
ws = await session.ws_connect(url)
except WSServerHandshakeError as e:
if e.message == 'Invalid response status':
# auth/path/proxy issue; inspect e.status, e.headers
raise RuntimeError(f'WS handshake failed with status {e.status}')
raise Prevention
- Confirm the endpoint and scheme (ws vs wss) with curl -i -H 'Connection: Upgrade' -H 'Upgrade: websocket'.
- Supply required auth headers in ws_connect(headers=...).
- Verify reverse-proxy Upgrade/Connection forwarding before relying on the endpoint.
When it happens
Trigger: Calling session.ws_connect against a URL that is a plain HTTP page, requires authentication (401/403), doesn't exist (404), or whose proxy stripped the Upgrade headers (200 with HTML). resp.status != 101.
Common situations: Wrong scheme (ws:// vs wss://). Endpoint path is a REST URL, not a WS endpoint. Bearer/basic auth required and not supplied. Nginx/Apache in front of the WS server without Upgrade/Connection forwarding. TLS termination that drops headers.
Related errors
- Connection lost
- Extension for deflate not supported
- Handshake error
- Invalid challenge response
- Invalid connection header
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/12f47db00eeee1fa.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/client.py:1101
)
# send request
resp = await self.request(
method,
url,
params=params,
headers=real_headers,
read_until_eof=False,
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(View on GitHub (pinned to d041d4d0fd)