docs/tools/browser-wsl2-windows-remote-cdp-troubleshooting.md
In the common split-host setup, OpenClaw Gateway runs inside WSL2, Chrome runs on Windows, and browser control must cross the WSL2 and Windows boundary. The layered failure pattern from issue #39369 means several independent problems can show up at once, which makes the wrong layer look broken first.
You have two valid patterns:
Use a remote browser profile that points from WSL2 to a Windows Chrome CDP endpoint.
Choose this when:
Use existing-session / user only when the Gateway itself runs on the same host as Chrome.
Choose this when:
responsebody, PDF
export, download interception, or batch actionsFor WSL2 Gateway + Windows Chrome, prefer raw remote CDP. Chrome MCP is host-local, not a WSL2-to-Windows bridge.
Reference shape:
127.0.0.1:18789http://127.0.0.1:18789/9222Several failures can overlap:
gateway.controlUi.allowedOrigins does not match the page originBecause of that, fixing one layer can still leave a different error visible.
When the UI is opened from Windows, use Windows localhost unless you have a deliberate HTTPS setup.
Use:
http://127.0.0.1:18789/
Do not default to a LAN IP for the Control UI. Plain HTTP on a LAN or tailnet address can trigger insecure-origin/device-auth behavior that is unrelated to CDP itself. See Control UI.
Work top to bottom. Do not skip ahead.
Start Chrome on Windows with remote debugging enabled:
chrome.exe --remote-debugging-port=9222
From Windows, verify Chrome itself first:
curl http://127.0.0.1:9222/json/version
curl http://127.0.0.1:9222/json/list
If this fails on Windows, OpenClaw is not the problem yet.
From WSL2, test the exact address you plan to use in cdpUrl:
curl http://WINDOWS_HOST_OR_IP:9222/json/version
curl http://WINDOWS_HOST_OR_IP:9222/json/list
Good result:
/json/version returns JSON with Browser / Protocol-Version metadata/json/list returns JSON (empty array is fine if no pages are open)If this fails:
Fix that before touching OpenClaw config.
For raw remote CDP, point OpenClaw at the address that is reachable from WSL2:
{
browser: {
enabled: true,
defaultProfile: "remote",
profiles: {
remote: {
cdpUrl: "http://WINDOWS_HOST_OR_IP:9222",
attachOnly: true,
color: "#00AA00",
},
},
},
}
Notes:
attachOnly: true for externally managed browserscdpUrl can be http://, https://, ws://, or wss:///json/versioncurl before expecting OpenClaw to succeedOpen the UI from Windows:
http://127.0.0.1:18789/
Then verify:
gateway.controlUi.allowedOrigins expectsHelpful page:
From WSL2:
openclaw browser open https://example.com --browser-profile remote
openclaw browser tabs --browser-profile remote
Good result:
openclaw browser tabs returns the targetsnapshot, screenshot, navigate) work from the same profileTreat each message as a layer-specific clue:
control-ui-insecure-auth
token_missing
pairing required
Remote CDP for profile "remote" is not reachable
cdpUrlBrowser attachOnly is enabled and CDP websocket for profile "remote" is not reachable
openclaw browser stop --browser-profile remotegateway timeout after 1500ms
No Chrome tabs found for profile="user"
curl http://127.0.0.1:9222/json/version work?curl http://WINDOWS_HOST_OR_IP:9222/json/version work?browser.profiles.<name>.cdpUrl use that exact WSL2-reachable address?http://127.0.0.1:18789/ instead of a LAN IP?existing-session across WSL2 and Windows instead of raw remote CDP?The setup is usually viable. The hard part is that browser transport, Control UI origin security, and token/pairing can each fail independently while looking similar from the user side.
When in doubt: