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

  1. Reconnect to Chrome's debug WebSocket and re-issue the command.
  2. Keep the target tab open for the duration of the automation session.
  3. Check Chrome's stderr/logs for crashes; increase stability (disable extensions, use a fresh profile).
  4. 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

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


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)