Kuberwastaken/claurst · error
CDP error
Error message
CDP error: {} What it means
Chrome replied to a CDP command with a JSON-RPC-style error object instead of a result. cdp_call detects val["error"] for the matching request id and surfaces the entire error value (method, message, data) via this anyhow message. The library itself is fine; Chrome rejected the command.
Solutions
- Read the embedded CDP error detail in the message; it names the failing method and reason.
- Re-query the DOM/target after navigation instead of reusing stale node ids or selectors.
- Connect to a page target rather than the browser/service-worker target for page commands.
- Check Chrome version compatibility for the CDP method being used.
Example fix
// before
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);
}
// after
// re-acquire fresh node references after any navigation before retrying DOM commands
let document = cdp_call(ws, "DOM.getDocument", json!({})).await?;
let node = cdp_call(ws, "DOM.querySelector", json!({"nodeId": document["result"]["root"]["nodeId"], "selector": sel})).await?; Defensive patterns
Strategy: try-catch
Validate before calling
// use a browser target (type=page) for page-level CDP commands
let targets: Vec<Value> = reqwest::get("http://localhost:9222/json").await?.json().await?;
assert!(targets.iter().any(|t| t["type"] == "page")); Type guard
fn is_cdp_error(val: &serde_json::Value) -> bool {
val.get("error").is_some()
} Try / catch
// match on the CDP error code for targeted recovery
if let Some(err) = val.get("error") {
if err["message"].as_str().unwrap_or("").contains("No node with given id") {
return Err(anyhow!("stale node id: re-run DOM.getDocument"));
}
return Err(anyhow!("CDP error: {}", err));
} Prevention
- Re-query DOM.getDocument / DOM.querySelector after every navigation
- Connect to 'page' targets, not browser or service-worker targets, for page commands
- Keep Chrome updated so the CDP surface matches the methods you call
When it happens
Trigger: In cdp_call, the response with the matching id contains an "error" key: e.g. CDP commands issued to the wrong target type, invalid parameters, "No node with given id" from stale DOM references, or Page.navigate to an unreachable URL.
Common situations: Calling Page/DOM commands on a target that doesn't support them (e.g. service worker or browser target); acting on a DOM node removed by a prior navigation; invalid selector or node id; protocol version mismatch between the client and Chrome.
Related errors
- WebSocket closed unexpectedly
- WebSocket closed by Chrome
- No debuggable page found on port
- WebSocket connect to
- No screenshot data in response
AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10).
Data as JSON: /api/errors/d699bf5fbee079f3.
Report an issue: GitHub.
Appendix: source
Thrown at src-rust/crates/commands/src/chrome.rs:82
// 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
// of the session, performing all async operations with it, then putting
// it back into the global.
// -----------------------------------------------------------------------
fn take_session() -> anyhow::Result<ChromeSession> {
SESSION.lock().take().ok_or_else(|| {
anyhow::anyhow!("No active Chrome session. Run `/chrome connect` first.")
View on GitHub (pinned to b0637c97ec)