{"record":{"id":"690a9cb76806bed1","repo":"BigPizzaV3/CodexPlusPlus","slug":"cdp-websocket-url-must-include-an-explicit-port-690a9c","errorCode":null,"errorMessage":"CDP WebSocket URL must include an explicit port","messagePattern":"CDP WebSocket URL must include an explicit port","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/cdp.rs","lineNumber":229,"sourceCode":"pub 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> {\n    let mut first_page = None;\n    for target in targets\n        .iter()\n        .filter(|target| is_injectable_page_target(target))\n    {\n        first_page.get_or_insert(target);\n        if is_primary_codex_page_target(target) {\n            return Ok(target.clone());\n        }\n    }\n","sourceCodeStart":211,"sourceCodeEnd":247,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/1f431ae49b57b3055e0e6845ba6156c6b4232b4d/crates/codex-plus-core/src/cdp.rs#L211-L247","documentation":"The port branch of validate_cdp_websocket_url (crates/codex-plus-core/src/cdp.rs:229) rejects ws/wss URLs without an explicit port. reqwest::Url knows default ports only for http/https/ftp, so port() is None for 'ws://127.0.0.1/devtools/...'. This validator is called from bridge.rs connect_cdp_websocket with the port it already parsed, so both sites fail together on the same input; this copy additionally enforces port == expected debug port afterwards.","triggerScenarios":"Calling validate_cdp_websocket_url() (directly or via bridge connection helpers) with a port-less ws URL such as ws://127.0.0.1/devtools/browser/<id>; the scheme and loopback-host checks have already passed when this fires.","commonSituations":"Stored/templated CDP URLs missing ':9222'; URL normalization that strips 'default-looking' ports; a custom debug port configuration where the URL was regenerated without the port field.","solutions":["Add the explicit port to the URL: ws://127.0.0.1:9222/devtools/browser/<id>","Use webSocketDebuggerUrl from http://127.0.0.1:<port>/json/version verbatim — it always includes the port","Validate user-supplied URLs early (scheme, host, port) and surface a clear message before the connect attempt"],"exampleFix":"# before\nws://127.0.0.1/devtools/browser/4f8a...\n\n# after\nws://127.0.0.1:9222/devtools/browser/4f8a...","handlingStrategy":"validation","validationCode":"// One shared pre-flight for all CDP ws URLs\nfn check_cdp_url(url: &str, expected_port: u16) -> anyhow::Result<()> {\n    let u = reqwest::Url::parse(url)?;\n    ensure!(matches!(u.scheme(), \"ws\" | \"wss\"), \"need ws/wss\");\n    ensure!(u.host_str().is_some(), \"need host\");\n    ensure!(u.port() == Some(expected_port), \"need explicit port == {expected_port}\");\n    validate_cdp_websocket_url(url, expected_port)\n}","typeGuard":"fn cdp_url_is_complete(url: &str, port: u16) -> bool {\n    reqwest::Url::parse(url).map(|u| {\n        matches!(u.scheme(), \"ws\" | \"wss\") && u.host_str().is_some() && u.port() == Some(port)\n    }).unwrap_or(false)\n}","tryCatchPattern":"if let Err(e) = validate_cdp_websocket_url(url, port) {\n    if e.to_string().contains(\"explicit port\") {\n        // configuration defect: reject loudly at load time, not at connect time\n        return Err(e.context(\"CDP URL config is missing ':port' — fix settings\"));\n    }\n    return Err(e);\n}","preventionTips":["Reject port-less ws URLs at config-load time with a clear message","Pin URLs to the known debug port when generating them","Add unit tests asserting Url::port() is Some for every stored CDP URL"],"tags":["cdp","websocket","url-parsing","port","validation"],"backgroundTag":"websocket-url-missing-port","analyzedSha":"1f431ae49b57b3055e0e6845ba6156c6b4232b4d","analyzedAt":"2026-08-16T20:54:18.598Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}