CDP Proxy fails to connect in WSL2: missing UUID in WebSocket path

Author: pine2DCreated Apr 9, 2026Updated May 30, 2026

Environment

  • OS: WSL2 (Ubuntu) connecting to Chrome on Windows host
  • Chrome: 146.0.7680.178 (Windows)
  • Node.js: v24.14.1
  • web-access: 2.4.2
  • Chrome launch: --remote-debugging-port=9222 --user-data-dir=D:\ChromeDebugProfile

Problem

CDP Proxy repeatedly fails with 连接错误: 连接失败 when running in WSL2.

Root cause: In WSL2, discoverChromePort() in cdp-proxy.mjs cannot find the DevToolsActivePort file because:

  1. The Linux paths (~/.config/google-chrome/DevToolsActivePort) don't exist — Chrome runs on Windows
  2. The win32 paths aren't checked because os.platform() returns linux in WSL

So it falls through to port scanning, which finds port 9222 but returns { port, wsPath: null }. Then getWebSocketUrl() builds ws://127.0.0.1:9222/devtools/browser without a UUID.

Modern Chrome (tested on 146) rejects WebSocket connections without the browser UUID in the path — resulting in a 1006 close code.

Reproduction

bash
# In WSL2, with Chrome remote debugging on Windows:

# This fails (no UUID):
node -e "const ws = new WebSocket('ws://127.0.0.1:9222/devtools/browser'); ws.onerror = e => console.log('FAIL', e.type); ws.onclose = e => console.log('code', e.code)"
# → FAIL error, code 1006

# This works (with UUID):
node -e "fetch('http://127.0.0.1:9222/json/version').then(r=>r.json()).then(j=>{const ws = new WebSocket(j.webSocketDebuggerUrl); ws.onopen = () => console.log('OK')})"
# → OK

Suggested Fix

After port scanning succeeds, fetch the UUID via HTTP API before returning:

javascript
// In discoverChromePort(), the port scanning fallback section:
for (const port of commonPorts) {
    const ok = await checkPort(port);
    if (ok) {
      console.log(`[CDP Proxy] 扫描发现 Chrome 调试端口: ${port}`);
      // Fetch UUID-bearing wsPath from HTTP API (required by modern Chrome)
      let wsPath = null;
      try {
        const resp = await fetch(`http://127.0.0.1:${port}/json/version`);
        const info = await resp.json();
        if (info.webSocketDebuggerUrl) {
          const url = new URL(info.webSocketDebuggerUrl);
          wsPath = url.pathname;
        }
      } catch (e) { /* fall through with null wsPath */ }
      return { port, wsPath };
    }
}

This is a minimal, non-breaking change — it only adds a /json/version HTTP call (not a WebSocket, so it won't trigger Chrome's authorization popup) as a fallback when DevToolsActivePort is unavailable.

Notes

  • The /json/version endpoint is a standard Chrome DevTools Protocol HTTP API, available whenever remote debugging is active
  • This fix also benefits any Linux environment where Chrome is started with --remote-debugging-port but DevToolsActivePort is missing or inaccessible
  • The check-deps.mjs script has the same DevToolsActivePort path limitation for WSL2, though it only affects port detection (not the connection failure)