Kuberwastaken/claurst · error
WebSocket closed by Chrome
Error message
WebSocket closed by Chrome
What it means
cdp_call received an explicit WebSocket Close frame while waiting for the CDP response. Unlike a silent stream end, Chrome actively closed the DevTools WebSocket connection. The library converts this into an anyhow error since the pending command can never complete.
Solutions
- Reconnect to Chrome's debug WebSocket and re-issue the command.
- Keep the target tab open for the duration of the automation session.
- Check Chrome's stderr/logs for crashes; increase stability (disable extensions, use a fresh profile).
- List available targets via http://localhost:9222/json and connect to an existing page target.
Example fix
// before
WsMessage::Close(_) => return Err(anyhow::anyhow!("WebSocket closed by Chrome")),
// after
WsMessage::Close(frame) => {
return Err(anyhow::anyhow!("WebSocket closed by Chrome: {:?}", frame.map(|f| f.code)));
} Defensive patterns
Strategy: fallback
Validate before calling
// confirm the target still exists before sending commands
let targets: Vec<Value> = reqwest::get("http://localhost:9222/json").await?.json().await?;
if !targets.iter().any(|t| t["id"] == target_id) {
bail!("target {} no longer exists", target_id);
} Try / catch
// on close, reconnect and re-attach to a live target
if let Err(e) = cdp_call(&mut ws, method, params).await {
if e.to_string().contains("closed by Chrome") {
let ws = connect_to_live_target().await?;
return cdp_call(ws, method, params).await;
}
return Err(e);
} Prevention
- Launch a dedicated Chrome instance for automation and keep it open
- Re-list targets after any navigation that may destroy the page
- Watch Chrome logs for crashes; use a clean profile without extensions
When it happens
Trigger: In the match on WsMessage inside cdp_call's drain loop, a Close(_) frame arrives before the response with the matching id: Chrome closed the socket (tab closed, browser exiting, target destroyed, or CDP session detached).
Common situations: User or automation closed the browser/tab during a command; Chrome crashed; the debugging session was invalidated by navigation that destroyed the target; timeout-based detachment by Chrome DevTools protocol.
Related errors
- WebSocket closed unexpectedly
- WebSocket connect to
- No debuggable page found on port
- CDP error
- No screenshot data in response
AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10).
Data as JSON: /api/errors/23fda5699a0cf28a.
Report an issue: GitHub.
Appendix: source
Thrown at src-rust/crates/commands/src/chrome.rs:75
ws: &mut WebSocketStream<MaybeTlsStream<TcpStream>>,
method: &str,
params: Value,
) -> anyhow::Result<Value> {
let id = next_id();
let request = json!({ "id": id, "method": method, "params": params });
ws.send(WsMessage::Text(request.to_string())).await?;
// Drain messages until we get the one with our id (ignore events).
loop {
let raw = ws
.next()
.await
.ok_or_else(|| anyhow::anyhow!("WebSocket closed unexpectedly"))??;
let text: String = match raw {
WsMessage::Text(t) => t.to_string(),
WsMessage::Ping(_) | WsMessage::Pong(_) => continue,
WsMessage::Close(_) => {
return Err(anyhow::anyhow!("WebSocket closed by Chrome"));
}
_ => continue,
};
let val: Value = serde_json::from_str(&text)?;
if val["id"] == id {
if let Some(err) = val.get("error") {
return Err(anyhow::anyhow!("CDP error: {}", err));
}
return Ok(val);
}
// It's an event or different response — keep waiting.
}
}
// -----------------------------------------------------------------------
// Session take/restore helpers
//
// We avoid holding a MutexGuard across await points by taking ownership
View on GitHub (pinned to b0637c97ec)