BigPizzaV3/CodexPlusPlus · error
CDP WebSocket URL has no host
Error message
CDP WebSocket URL has no host
What it means
`validate_cdp_websocket_url` enforces that CDP WebSocket URLs use ws/wss, point at a loopback IP, and match the expected debug port. This error is raised when `Url::host_str()` returns None, i.e. the parsed URL has no host component. It prevents connecting to CDP endpoints that aren't a concrete loopback address.
Solutions
- Pass a full absolute URL including host, e.g. ws://127.0.0.1:9222/devtools/browser/<id>
- Ensure the host is a literal loopback IP (127.0.0.1 or ::1) — hostnames like localhost are also rejected later
- Check config for empty host placeholders and fill them from the detected debug port
- Pre-parse with Url::parse yourself and log the components to catch truncation early
Example fix
// before
let url = format!("ws:///devtools/browser/{id}"); // missing host
// after
let url = format!("ws://127.0.0.1:{debug_port}/devtools/browser/{id}"); Defensive patterns
Strategy: validation
Validate before calling
let u = reqwest::Url::parse(&url)?;
if u.scheme() != "ws" && u.scheme() != "wss" { bail!("must be ws/wss"); }
if u.host_str().is_none() { bail!("CDP URL needs a host: {url}"); } Try / catch
if let Err(e) = validate_cdp_websocket_url(&url, port) {
log::warn!("rejecting CDP URL {url}: {e:#}");
return Ok(None); // treat as no CDP target rather than propagating
} Prevention
- Always include a literal loopback host (127.0.0.1 / ::1)
- Never interpolate empty host variables into URL templates
- Validate URLs from config before passing to bridge APIs
When it happens
Trigger: Calling `validate_cdp_websocket_url` (directly or via `browser_identity`, `query_targets_url`, `response_contains_codex_target`, or bridge connect) with a URL whose host is absent — e.g. relative path input, `ws:///devtools/...`, or a non-URL string that still parses oddly.
Common situations: Template string left with an empty host placeholder, URL truncated during config serialization, or passing a path like `/devtools/browser/id` instead of a full ws:// URL.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
- CDP WebSocket URL must include an explicit port
- CDP WebSocket URL must include an explicit port
- CDP websocket closed before response for
- CDP WebSocket host must be loopback
- selected CDP target has no websocket URL
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/254afc97b6c8ab95.
Report an issue: GitHub.
Appendix: source
Thrown at crates/codex-plus-core/src/cdp.rs:218
.context("failed to deserialize CDP targets")?;
for target in &targets {
if let Some(websocket_url) = target.web_socket_debugger_url.as_deref() {
validate_cdp_websocket_url(websocket_url, debug_port).with_context(|| {
format!("unsafe CDP target WebSocket URL for target {}", target.id)
})?;
}
}
Ok(targets)
}
pub fn validate_cdp_websocket_url(url: &str, expected_port: u16) -> anyhow::Result<()> {
let parsed = reqwest::Url::parse(url).context("invalid CDP WebSocket URL")?;
if !matches!(parsed.scheme(), "ws" | "wss") {
bail!("CDP WebSocket URL must use ws or wss");
}
let host = parsed
.host_str()
.ok_or_else(|| anyhow::anyhow!("CDP WebSocket URL has no host"))?;
let address = host
.trim_start_matches('[')
.trim_end_matches(']')
.parse::<IpAddr>()
.with_context(|| "CDP WebSocket host must be a loopback IP address")?;
if !address.is_loopback() {
bail!("CDP WebSocket host must be loopback");
}
let port = parsed
.port()
.ok_or_else(|| anyhow::anyhow!("CDP WebSocket URL must include an explicit port"))?;
if port != expected_port {
bail!("CDP WebSocket port {port} does not match debug port {expected_port}");
}
Ok(())
}
pub fn pick_page_target(targets: &[CdpTarget]) -> anyhow::Result<CdpTarget> {View on GitHub (pinned to b1ed92e5e4)