Diagnosing a wedged Chrome DevTools socket with a raw WebSocket probe
Symptom: every Chrome DevTools Protocol client command fails with context deadline exceeded, but the browser's HTTP endpoints look perfectly healthy —
curl http://localhost:9222/json/version and /json/list both respond.
That combination is ambiguous: it can't tell you whether the client tooling is
broken or the browser is. The error message is identical either way, and it's
easy to conclude "the tool is broken" when it isn't.
To disambiguate, bypass the tooling entirely and speak the protocol by hand.
Grab a target's webSocketDebuggerUrl from /json/list, then send one
command over a bare WebSocket:
import asyncio, json, websockets
async def probe(ws_url):
async with websockets.connect(ws_url, max_size=None) as ws:
await ws.send(json.dumps({
"id": 1,
"method": "Runtime.evaluate",
"params": {"expression": "1+1"},
}))
print(await asyncio.wait_for(ws.recv(), timeout=5))
asyncio.run(probe("ws://127.0.0.1:9222/devtools/page/<target-id>"))
Two outcomes, two very different conclusions:
- Reply arrives — the browser is fine; debug the client.
- Handshake succeeds but the reply never comes — the browser's DevTools
socket is wedged. No client will work against it, and no amount of client
debugging will help.
In the wedged case, don't fight it: launch a fresh Chrome on another port and
point the tooling there.
chrome --headless=new --remote-debugging-port=9223 \
--user-data-dir=/tmp/fresh-profile --window-size=1440,1400
A five-line probe settles in seconds what error messages alone never will.