BigPizzaV3/CodexPlusPlus · error · anyhow::Error
CDP websocket closed before response for
Error message
CDP websocket closed before response for {method} id {message_id} What it means
wait_for_id pumps incoming CDP messages until a response whose id matches the pending command arrives. If next_message() returns None — the websocket stream ended — any still-pending command can never be answered, so the session bails with "CDP websocket closed before response for {method} id {message_id}". It reports the method and message id of the abandoned command.
Solutions
- Reconnect to the CDP websocket (connect_cdp_websocket) and re-issue the command in a fresh session
- Check the target is still alive (correct websocket_url, tab not closed/crashed) before sending
- Wrap CDP session usage in retry-once logic that re-establishes the connection on this error
- For long operations, keep the target page open and avoid navigation that invalidates the session
Example fix
// before
let resp = session.send_command(id, method, params).await?;
// after
let resp = match session.send_command(id, method, params).await {
Ok(r) => r,
Err(e) if e.to_string().contains("websocket closed") => {
session = reconnect(websocket_url).await?;
session.send_command(id, method, params).await?
}
}; Defensive patterns
Strategy: retry
Validate before calling
// confirm the CDP endpoint is alive before sending
let ws_ok = tokio_tungstenite::connect_async(&websocket_url).await.is_ok();
if !ws_ok { eprintln!("CDP endpoint unreachable"); } Try / catch
match session.send_command(id, method, params).await {
Ok(r) => r,
Err(e) if e.to_string().contains("websocket closed before response") => {
session = CdpSession::new(connect_cdp_websocket(&websocket_url).await?);
session.send_command(id, method, params).await?
}
Err(e) => return Err(e.into()),
} Prevention
- Keep the target tab/app open for the duration of automation
- Reconnect and retry once on closed-socket errors before surfacing failure
- Monitor the target process/tab lifecycle; long commands are the most exposed to drops
When it happens
Trigger: The browser/tab closed, the debug port dropped the connection, or the page navigated in a way that tore down the CDP session while send_command was awaiting its response.
Common situations: Target tab was closed or crashed during a long-running command; DevTools remote debugging port dropped; the app under automation exited; network interruption to the CDP endpoint; awaiting a command the target never answers and the socket then closes.
Understand the failure class
Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.
Related errors
- CDP command id failed
- CDP WebSocket URL has no host
- CDP WebSocket URL must include an explicit port
- CDP WebSocket URL must include an explicit port
- Page.captureScreenshot returned invalid PNG data
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/3b0ef79564b789ce.
Report an issue: GitHub.
Appendix: source
Thrown at crates/codex-plus-core/src/bridge.rs:716
"method": method,
"params": params,
})
.to_string()
.into(),
))
.await
.with_context(|| format!("failed to send CDP command {method} id {message_id}"))?;
Ok(())
}
async fn wait_for_id(&mut self, message_id: u64, method: String) -> anyhow::Result<Value> {
loop {
if let Some(response) = self.responses.remove(&message_id) {
return command_result(response, &method, message_id);
}
let Some(message) = self.next_message().await? else {
bail!("CDP websocket closed before response for {method} id {message_id}");
};
if let Some(response_id) = message.get("id").and_then(Value::as_u64) {
if response_id == message_id {
return command_result(message, &method, message_id);
}
self.responses.insert(response_id, message);
}
}
}
async fn next_message(&mut self) -> anyhow::Result<Option<Value>> {
let Some(message) = self.socket.next().await else {
return Ok(None);
};
let message = message.context("failed to read CDP websocket message")?;
let Message::Text(text) = message else {
return Ok(Some(json!({})));View on GitHub (pinned to b1ed92e5e4)