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
- Append the remote-debugging port explicitly: ws://127.0.0.1:9222/devtools/...
- Take the URL verbatim from /json/version's webSocketDebuggerUrl, which always includes the port
- Pass the same port value you launched Chrome with (`--remote-debugging-port`) as `expected_port`
- 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
- Format the port into every ws:// URL you construct
- Launch Chrome with --remote-debugging-port and reuse that exact port
- Avoid URL normalization that drops default ports before validation
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
- CDP WebSocket URL has no host
- CDP WebSocket URL must include an explicit port
- CDP websocket closed before response for
- selected CDP target has no websocket URL
- app-server 未连接
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)