{"record":{"id":"254afc97b6c8ab95","repo":"BigPizzaV3/CodexPlusPlus","slug":"cdp-websocket-url-has-no-host","errorCode":null,"errorMessage":"CDP WebSocket URL has no host","messagePattern":"CDP WebSocket URL has no host","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/cdp.rs","lineNumber":218,"sourceCode":"        .context(\"failed to deserialize CDP targets\")?;\n    for target in &targets {\n        if let Some(websocket_url) = target.web_socket_debugger_url.as_deref() {\n            validate_cdp_websocket_url(websocket_url, debug_port).with_context(|| {\n                format!(\"unsafe CDP target WebSocket URL for target {}\", target.id)\n            })?;\n        }\n    }\n    Ok(targets)\n}\n\npub fn validate_cdp_websocket_url(url: &str, expected_port: u16) -> anyhow::Result<()> {\n    let parsed = reqwest::Url::parse(url).context(\"invalid CDP WebSocket URL\")?;\n    if !matches!(parsed.scheme(), \"ws\" | \"wss\") {\n        bail!(\"CDP WebSocket URL must use ws or wss\");\n    }\n    let host = parsed\n        .host_str()\n        .ok_or_else(|| anyhow::anyhow!(\"CDP WebSocket URL has no host\"))?;\n    let address = host\n        .trim_start_matches('[')\n        .trim_end_matches(']')\n        .parse::<IpAddr>()\n        .with_context(|| \"CDP WebSocket host must be a loopback IP address\")?;\n    if !address.is_loopback() {\n        bail!(\"CDP WebSocket host must be loopback\");\n    }\n    let port = parsed\n        .port()\n        .ok_or_else(|| anyhow::anyhow!(\"CDP WebSocket URL must include an explicit port\"))?;\n    if port != expected_port {\n        bail!(\"CDP WebSocket port {port} does not match debug port {expected_port}\");\n    }\n    Ok(())\n}\n\npub fn pick_page_target(targets: &[CdpTarget]) -> anyhow::Result<CdpTarget> {","sourceCodeStart":200,"sourceCodeEnd":236,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-core/src/cdp.rs#L200-L236","documentation":"`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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nlet url = format!(\"ws:///devtools/browser/{id}\"); // missing host\n// after\nlet url = format!(\"ws://127.0.0.1:{debug_port}/devtools/browser/{id}\");","handlingStrategy":"validation","validationCode":"let u = reqwest::Url::parse(&url)?;\nif u.scheme() != \"ws\" && u.scheme() != \"wss\" { bail!(\"must be ws/wss\"); }\nif u.host_str().is_none() { bail!(\"CDP URL needs a host: {url}\"); }","typeGuard":null,"tryCatchPattern":"if let Err(e) = validate_cdp_websocket_url(&url, port) {\n    log::warn!(\"rejecting CDP URL {url}: {e:#}\");\n    return Ok(None); // treat as no CDP target rather than propagating\n}","preventionTips":["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"],"tags":["cdp","url-validation","websocket","security"],"backgroundTag":"invalid-url-format","analyzedSha":"b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6","analyzedAt":"2026-09-19T23:35:21.129Z","contentChangedAt":"2026-09-19T23:35:21.129Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}