BigPizzaV3/CodexPlusPlus · error

CDP WebSocket URL must include an explicit port

Error message

CDP WebSocket URL must include an explicit port

What it means

In the same `validate_cdp_websocket_url` guard, after scheme and loopback-host checks the URL must carry an explicit port, which is compared with the expected Chrome remote-debugging port. `Url::port()` returns None when the port is omitted (or is the scheme default), so this error fires for portless ws:// URLs. It guarantees the caller is talking to the exact debug endpoint.

Solutions

  1. Append the remote-debugging port explicitly: ws://127.0.0.1:9222/devtools/...
  2. Take the URL verbatim from /json/version's webSocketDebuggerUrl, which always includes the port
  3. Pass the same port value you launched Chrome with (`--remote-debugging-port`) as `expected_port`
  4. If using a default scheme port, serialize it explicitly since Url::port() collapses it to None

Example fix

// before
let url = format!("ws://{host}/devtools/page/{target}");
// after
let url = format!("ws://{host}:{port}/devtools/page/{target}");
Defensive patterns

Strategy: validation

Validate before calling

let u = reqwest::Url::parse(&url)?;
if u.port().is_none() { bail!("CDP URL needs explicit port: {url}"); }
if u.port() != Some(expected_port) { bail!("port mismatch"); }

Try / catch

match validate_cdp_websocket_url(&url, debug_port) {
    Err(e) if e.to_string().contains("explicit port") => bail!("bad CDP URL, expected :{debug_port}"),
    Err(e) => return Err(e),
    Ok(()) => proceed(),
}

Prevention

When it happens

Trigger: Calling `validate_cdp_websocket_url` with `ws://127.0.0.1/devtools/...` (no `:port`), or a URL whose port was normalized away (e.g. `:443` on wss), via any of its callers (`query_targets_url`, `browser_identity`, tests, bridge connect).

Common situations: Constructing the debugger URL from a template without substituting the port, copying an URL and losing `:9222`, or normalization code that drops default ports.

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


AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19). Data as JSON: /api/errors/690a9cb76806bed1. Report an issue: GitHub.

Appendix: source

Thrown at crates/codex-plus-core/src/cdp.rs:229

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> {
    let mut first_page = None;
    for target in targets
        .iter()
        .filter(|target| is_injectable_page_target(target))
    {
        first_page.get_or_insert(target);
        if is_primary_codex_page_target(target) {
            return Ok(target.clone());
        }
    }

View on GitHub (pinned to b1ed92e5e4)