Hi, following on from earlier help from Krill today on WSL2 + CDP browser setup. We got openclaw browser --browser-profile brave tabs working successfully, but screenshot is timing out. We then installed Playwright via the bundled CLI:
node node_modules/playwright-core/cli.js install chromium
That completed successfully and downloaded Chromium to C:\Users\User1\AppData\Local\ms-playwright\chromium-1217.
Two problems now:
Problem 1 â Config overwrite on every node start
Every time openclaw node run --host 127.0.0.1 --port 18789 runs on Windows, it shows Config overwrite: C:\Users\User1.openclaw\openclaw.json and rewrites the file. Our browser brave profile (existing-session driver, Brave User Data dir) keeps getting removed. After the overwrite, profiles=2 instead of profiles=3, meaning brave is gone.
Problem 2 â Node hangs on startup
After installing Playwright, the node now hangs indefinitely on startup and never reaches Browser control service ready. It just shows the lobster tagline and stops. Was working before Playwright install.
Setup: OpenClaw 2026.4.11, Windows 11, WSL2 Ubuntu 24.04. Gateway runs in WSL2, Windows node connects to it as remote. Brave is open with remote debugging enabled at brave://inspect/#remote-debugging (confirmed listening on 127.0.0.1:9222).
What's the correct way to persist the brave profile so config overwrite doesn't remove it, and what's causing the node to hang after Playwright install?
#Windows node hung on startup + config overwrite removing browser profiles
1 messages ¡ Page 1 of 1 (latest)
Youâve got two separate things mixed together here.
- The
braveprofile persistence issue - The ânode hangs on startupâ issue
1) Why brave keeps disappearing
In your setup, the Windows node is the machine that owns the browser.
So if you want an existing-session Brave profile, it must live in the Windows nodeâs own:
C:\Users\User1\.openclaw\openclaw.json
Not in WSL2, and not via a proxy-created profile.
Current source also explicitly blocks persistent profile mutation through the node browser proxy (browser.proxy cannot mutate persistent browser profiles), so the safe/durable path is:
- edit the Windows node config directly, or
- run
openclaw browser create-profile ...locally on Windows, not from the WSL2 side
A correct Windows-side profile looks like:
{
browser: {
enabled: true,
profiles: {
brave: {
driver: "existing-session",
attachOnly: true,
userDataDir: "C:\\Users\\User1\\AppData\\Local\\BraveSoftware\\Brave-Browser\\User Data",
color: "#FB542B"
}
}
}
}
If runtime says profiles=2, that usually means only the built-ins are present (openclaw + user), so brave is not actually being loaded from the Windows nodeâs local config.
Also, the Config overwrite: C:\Users\User1\.openclaw\openclaw.json message is a generic config write log. It is not node-host identity state, that lives in node.json, not openclaw.json.
2) Why openclaw node run looks âhungâ
This is probably not a real hang.
openclaw node run --host 127.0.0.1 --port 18789 is a foreground long-running process. After startup it is expected to just sit there and wait.
Also, Browser control service ready is not a normal node run startup banner. That log comes from the browser control service when it is actually started for browser work.
So âlobster tagline and then nothingâ is usually just the node host idling normally.
3) About the Playwright install
node node_modules/playwright-core/cli.js install chromium only downloads a Chromium binary.
It does not install the full playwright package, and it should not be the thing causing node run to âhangâ.
Also, per the docs, page screenshots for existing-session can work without full Playwright. So your screenshot timeout was probably not caused by missing Playwright in the first place.
What does require full Playwright is stuff like:
navigateact- AI / role snapshots
- element screenshots
Docs:
- https://docs.openclaw.ai/tools/browser#playwright-requirement
- https://docs.openclaw.ai/tools/browser-wsl2-windows-remote-cdp-troubleshooting
- https://docs.openclaw.ai/tools/browser#node-browser-proxy-zero-config-default
What Iâd recommend
For your exact setup, pick one of these:
- Best if you want real Windows Brave session: keep the Windows node host, persist
bravein the Windows config locally - Best if you want the simplest split-host setup: use a raw remote CDP profile on WSL2 instead of
existing-session
If you want, paste the exact browser block from the Windows openclaw.json, and Iâll sanity check it line by line.
Here is the browser block from the Windows openclaw.json:
json"browser": {
"enabled": true,
"profiles": {
"brave": {
"driver": "existing-session",
"attachOnly": true,
"color": "#FB542B",
"userDataDir": "C:\Users\User1\AppData\Local\BraveSoftware\Brave-Browser\User Data"
}
}
}
Config looks correct but node still shows profiles=2 on startup, meaning brave isn't being loaded. Brave is open with remote debugging enabled at brave://inspect/#remote-debugging, confirmed listening on 127.0.0.1:9222. What are we missing?
That browser block itself looks valid.
So if the runtime still says profiles=2, the problem is probably not the brave stanza, itâs that the process reading config is not actually seeing that file contents.
A few important bits:
resolveBrowserConfig()would turn your config into 3 profiles:openclaw,user, andbrave- so
profiles=2meanscfg.browser.profiles.bravewas missing by the time it was loaded - also,
127.0.0.1:9222is not used bydriver: "existing-session"anyway, that port matters for raw remote CDP, not Chrome MCP existing-session attach
Most likely causes now
- Wrong config file / wrong user / env override
OPENCLAW_CONFIG_PATHOPENCLAW_STATE_DIR- running under a different Windows user/profile than the file you edited
-
The full JSON file is malformed
- your pasted snippet starts with
json"browser"... - if
jsonis literally in the file from a bad paste, that would absolutely explain this - same if thereâs broken syntax earlier in the file, even if the
browserblock itself is fine
- your pasted snippet starts with
-
Youâre reading the node host banner as startup
openclaw node runnormally sits in the foreground and waitsBrowser control service readyis from the browser service when it is actually started, not the normal node-host idle banner
Best next checks, on Windows, in the same shell where you run openclaw node run
PowerShell:
$env:OPENCLAW_CONFIG_PATH
$env:OPENCLAW_STATE_DIR
openclaw browser profiles
What Iâd expect:
- if
openclaw browser profilesshows onlyopenclaw+user, then itâs definitely a config path / user / parse problem on Windows - if it shows
bravelocally, but the node path still doesnât, then weâre looking at a different runtime context
One more key point
For your setup, you should choose one mode and stick to it:
- existing-session via Windows node host â use the local Windows
browser.profiles.brave - raw CDP from WSL2 to Windows â use a
cdpUrlprofile on WSL2, notexisting-session
Docs:
- https://docs.openclaw.ai/tools/browser#node-browser-proxy-zero-config-default
- https://docs.openclaw.ai/tools/browser-wsl2-windows-remote-cdp-troubleshooting
- https://docs.openclaw.ai/tools/browser#profiles-openclaw-vs-user
If you want, paste the first ~30 lines of the full Windows openclaw.json plus the output of openclaw browser profiles on Windows, that should narrow it down fast.
We've got openclaw browser --browser-profile brave tabs working from WSL2 â it lists all open Brave tabs. But openclaw browser profiles from the Windows side times out with "gateway timeout after 45000ms", and openclaw browser --browser-profile brave screenshot from WSL2 also times out. The node shows profiles=2 on startup, not 3, so brave profile isn't loading. The node itself appears to be running (lobster tagline, then idle). What's causing the gateway timeout from the Windows side, and why isn't the brave profile being picked up?
The new clue is this:
openclaw browser --browser-profile brave tabsfrom WSL2 works
That means the brave profile is being resolved for that request. So I donât think the core problem is âWindows never loaded braveâ.
Whatâs probably happening
-
profiles=2is a red herringresolveBrowserConfig()always injectsopenclaw+user- if you were really running against the Windows
braveconfig, youâd expect 3 - but since
tabswith--browser-profile braveworks, that count is likely coming from the wrong browser-control instance, probably the gateway-side local browser service, not the node-backed one you care about
-
openclaw browser profileson Windows is not a direct local node-host inspection- it still goes through the gateway browser surface
- in your split-host setup that means gateway â node proxy â browser control
- so
gateway timeout after 45000msjust means that proxied request stalled, not that the Windows config file was definitely unreadable
9222is not part ofexisting-sessiondriver: "existing-session"uses Chrome MCP attach127.0.0.1:9222matters for raw remote CDP- so donât use the remote-debugging port as proof that the
existing-sessionpath is healthy
So whatâs the likely real issue?
Not Playwright, and probably not profile persistence.
More likely:
- Chrome MCP over the node proxy is healthy enough for
tabs - but it is timing out on heavier routes like
profilesandscreenshot
That also matches the docs pretty well: for WSL2 Gateway + Windows browser, the recommended path is still raw remote CDP, not existing-session, unless you really need the local signed-in browser path via node host.
My recommendation
If the goal is reliable screenshots/navigation from WSL2, I would stop fighting existing-session here and switch to a raw remote CDP profile on the WSL2 side:
- use a WSL2-reachable Windows address for
cdpUrl - keep
attachOnly: true - use that profile for screenshot/snapshot/navigation
That is the path the troubleshooting doc recommends for this architecture:
https://docs.openclaw.ai/tools/browser-wsl2-windows-remote-cdp-troubleshooting
And this is the key distinction:
https://docs.openclaw.ai/tools/browser#profiles-openclaw-vs-user
Short version
brave tabsworking means the profile is probably not the thing thatâs missing- the Windows
profilestimeout is a gateway/proxy timeout, not a clean local config check 9222is irrelevant forexisting-session
- your real problem is probably Chrome MCP over node proxy timing out
- for this split-host setup, raw remote CDP is the more reliable route
If you want, I can write the exact WSL2-side remote profile block to use instead.
Yes please, give us the exact WSL2-side remote CDP profile block for our setup. Windows host IP from WSL2 is 172.22.240.1, Brave is running with remote debugging on 127.0.0.1:9222.
Yep, for WSL2 Gateway â Windows Brave via raw remote CDP, Iâd use a new profile name so itâs clearly separate from the old existing-session setup.
Put this in the WSL2 ~/.openclaw/openclaw.json:
{
browser: {
enabled: true,
defaultProfile: "windows-brave",
ssrfPolicy: {
dangerouslyAllowPrivateNetwork: true,
},
profiles: {
"windows-brave": {
cdpUrl: "http://172.22.240.1:9222",
attachOnly: true,
color: "#FB542B",
},
},
},
}
A few important notes:
- use
172.22.240.1, not127.0.0.1, on the WSL2 side - this is a raw remote CDP profile, so no
driver: "existing-session" - after changing browser config, restart the WSL2 gateway
Before testing OpenClaw, verify WSL2 can actually reach Brave:
curl http://172.22.240.1:9222/json/version
curl http://172.22.240.1:9222/json/list
If those fail, the profile will fail too, which means Windows is still only exposing the debug port locally.
Then test:
openclaw browser tabs --browser-profile windows-brave
openclaw browser screenshot --browser-profile windows-brave
Docs:
If you want, I can also give you the Windows-side Brave launch command/checklist to make sure 9222 is reachable from WSL2, because thatâs the next likely gotcha.
Yes please, give us the Windows-side Brave launch checklist to make sure port 9222 is reachable from WSL2. We already tried --remote-debugging-address=0.0.0.0 earlier but Brave ignored it and stayed on 127.0.0.1:9222 only. And New-NetFirewallRule gave us "Access is denied" even in an elevated PowerShell. We need an admin-free way to expose the port to WSL2.
Short version, if Brave is only listening on 127.0.0.1:9222 on Windows, then WSL2 raw remote CDP is blocked at the Windows networking layer.
I donât know a reliable admin-free way around that in this setup.
Windows-side checklist
- Fully quit Brave
- Start Brave with remote debugging enabled
- On Windows, verify:
curl http://127.0.0.1:9222/json/version
curl http://127.0.0.1:9222/json/list
- On WSL2, verify the Windows host IP:
curl http://172.22.240.1:9222/json/version
curl http://172.22.240.1:9222/json/list
How to interpret it
-
Windows works, WSL2 fails
Then OpenClaw is not the blocker. The port is still loopback-only on Windows. -
WSL2 works too
Then the remote CDP profile should work.
Important part
If:
--remote-debugging-address=0.0.0.0is ignored, and- firewall / port-forwarding changes are blocked,
then raw remote CDP from WSL2 is basically not available without admin help.
That leaves you with the practical options:
-
Keep the Windows node-host + existing-session path
No 9222 exposure needed, but youâre dealing with the Chrome MCP timeout path. -
Run the Gateway on Windows instead of WSL2
Thenexisting-sessionstays host-local. -
Get admin help to expose/proxy 9222 to WSL2
That is the path needed for raw remote CDP.
So Iâd be direct here: this is no longer an OpenClaw config problem, itâs a Windows loopback exposure problem.
Docs:
If you want, I can help you pick the least painful of those 3 options.
Quick status update. Firewall rule is now created successfully (had to use proper admin terminal). Brave is launching with --remote-debugging-port=9222. But openclaw node run is hanging again on startup â shows the lobster tagline and nothing else for several minutes, never reaches Browser control service ready. Previously it did show profiles=3 once but then the node stopped. Before we try anything else, can you tell us: what should we verify step by step to confirm Brave is actually listening on 9222 and reachable from WSL2, and what does a healthy node startup look like in this setup?
Yep. The big thing now is: if youâre testing the raw remote CDP path, you do not need openclaw node run at all.
So Iâd verify it in this order:
1) Verify Brave locally on Windows
In Windows:
curl http://127.0.0.1:9222/json/version
curl http://127.0.0.1:9222/json/list
Healthy result:
/json/versionreturns JSON with fields likeBrowser,Protocol-Version,webSocketDebuggerUrl/json/listreturns JSON, even[]is fine
If this fails, stop there. Brave is not serving CDP yet.
2) Verify WSL2 can reach Windows
In WSL2:
curl http://172.22.240.1:9222/json/version
curl http://172.22.240.1:9222/json/list
Healthy result:
- same idea, JSON comes back from both
- if Windows works but WSL2 fails, itâs still a Windows reachability problem, not OpenClaw
3) Verify OpenClaw from WSL2, no node host involved
With the WSL2-side remote profile:
openclaw browser tabs --browser-profile windows-brave
If that works, then OpenClaw can see the remote CDP endpoint.
Then try:
openclaw browser screenshot --browser-profile windows-brave
What a healthy openclaw node run looks like
For the node-host path, healthy startup is actually pretty boring:
- it prints the startup banner
- it stays in the foreground
- it mostly sits there idle waiting for work
It does not need to print Browser control service ready on startup.
That message is from the browser control service when that service is actually started for browser work. So âlobster tagline, then nothingâ is often just normal idle node-host behavior, not a hang.
For your current test, Iâd simplify hard
If youâre pursuing raw remote CDP, ignore the node completely for now.
The clean test sequence is:
- Windows
curl 127.0.0.1:9222 - WSL2
curl 172.22.240.1:9222 - WSL2
openclaw browser tabs --browser-profile windows-brave
If you want, after you run those 3 checks, paste the exact outputs and I can tell you which layer is still broken.
Docs: https://docs.openclaw.ai/tools/browser-wsl2-windows-remote-cdp-troubleshooting
Tabs work perfectly with --browser-profile windows-brave. Screenshot still times out with "gateway timeout after 20000ms". We've now installed Playwright via the bundled CLI on both Windows and WSL2. curl from WSL2 to 172.22.240.1:9222 returns JSON correctly. Gateway restarts cleanly. What's causing screenshot to time out when tabs works fine?
The reason is that tabs and screenshot are exercising different parts of CDP.
tabsonly needs the HTTP discovery endpoints like/json/listscreenshotneeds the per-tab DevTools WebSocket and then a livePage.captureScreenshotcall
So this combination:
curl http://172.22.240.1:9222/json/versionworksopenclaw browser tabs --browser-profile windows-braveworksopenclaw browser screenshot --browser-profile windows-bravetimes out
usually means:
HTTP CDP is reachable, but the per-tab WebSocket/control path is stalling
Thatâs why tabs can succeed while screenshot still fails.
Two important clarifications
-
The bundled CLI you ran installs the Chromium browser binary, not the full
playwrightnpm package.
So that install does not explain or fix this timeout. -
This is now probably not a config / firewall / node-startup problem anymore.
Itâs much narrower: remote CDP WebSocket attach or screenshot command path.
What Iâd check next
1) Confirm /json/list includes webSocketDebuggerUrl
From WSL2:
curl http://172.22.240.1:9222/json/list
For the page you want to capture, make sure there is a webSocketDebuggerUrl.
2) Clear any stale browser control session
From WSL2:
openclaw browser stop --browser-profile windows-brave
Then retry screenshot.
Docs note that for attach-only / remote CDP profiles, stop clears the active control session and temporary emulation state.
3) Raise the remote WebSocket handshake timeout
On the WSL2 gateway config, try increasing:
{
browser: {
remoteCdpTimeoutMs: 5000,
remoteCdpHandshakeTimeoutMs: 10000
}
}
Then restart the gateway.
That setting is specifically for the remote CDP WebSocket handshake path.
The likely root cause
At this point Iâd summarize it as:
- discovery works
- tab listing works
- actual tab-control websocket for screenshot is timing out
So the weak link is the remote DevTools websocket path, not the basic CDP HTTP endpoint.
Docs:
- https://docs.openclaw.ai/tools/browser-wsl2-windows-remote-cdp-troubleshooting
- https://docs.openclaw.ai/tools/browser
If you want, paste one sanitized /json/list entry for a page tab, and I can tell you whether the websocket URL looks right.
Followed all three steps. /json/list confirms all tabs have webSocketDebuggerUrl pointing to ws://172.22.240.1:9222. Ran openclaw browser stop then screenshot â gateway crashed with "abnormal closure, no close reason". Added remoteCdpTimeoutMs: 5000 and remoteCdpHandshakeTimeoutMs: 10000, restarted gateway â back to "gateway timeout after 20000ms". Tabs still work fine. Screenshot consistently fails. What's causing the gateway to crash or timeout specifically on screenshot?
At this point, the pattern is pretty clear:
/json/versionworks/json/listworks- tab listing works
- every tab has a valid
webSocketDebuggerUrl - only screenshot fails, either with
abnormal closure, no close reasonor a 20s timeout
That strongly points to the failure being inside the screenshot CDP path itself:
- screenshot is opening the per-tab DevTools websocket
- then issuing
Page.captureScreenshot - and that websocket is either being closed unexpectedly or the CDP call is never returning
So this is not a profile-loading problem anymore, and probably not a plain reachability problem either.
Whatâs most likely causing it
A bug / incompatibility in this exact path:
WSL2 Gateway â Windows Brave remote CDP â per-tab websocket screenshot
In other words, discovery is fine, but the actual screenshot command path is not stable.
Why tabs still work
Because tabs is much lighter. It only proves the JSON discovery layer is healthy.
It does not prove that Brave will successfully keep a remote DevTools websocket alive through Page.captureScreenshot.
What Iâd do next
- Try the same setup with Chrome or Edge instead of Brave
- if screenshot works there, this is very likely Brave-specific
- if it fails there too, itâs likely a broader remote CDP on Windows from WSL2 issue
-
If you need a working path right now
- use the Windows-local gateway / existing-session path, or
- keep the node-host approach instead of raw remote CDP
-
If it reproduces consistently, file a GitHub issue
Include:openclaw --version(2026.4.11)- Windows 11 + WSL2 Ubuntu 24.04
curl /json/versionworkscurl /json/listworks- tabs work
- screenshot fails with
gateway timeout after 20000ms - sometimes
abnormal closure, no close reason
Issue tracker: https://github.com/openclaw/openclaw/issues
So, bluntly, I think youâve already isolated this pretty well. It looks like a remote screenshot bug, not a bad config.
One more issue. Browser plugin is enabled (plugins.entries.browser.enabled: true), remote CDP profile is configured and working (tabs list fine from CLI). But the browser tool is not showing up in Steve's agent sessions at all â he consistently says "no browser tool exposed in this session." Tools profile is coding. How do we get the browser tool actually exposed to the agent?
Yep, this one is simpler.
The key bit is:
tools.profile: "coding"does not includebrowser
The coding profile includes:
group:fsgroup:runtimegroup:webgroup:sessionsgroup:memory- some media/cron tools
But browser lives in group:ui, so Steve wonât see it unless you explicitly allow it.
Fix
Either add just browser:
{
tools: {
profile: "coding",
allow: ["browser"]
}
}
Or add the whole UI group:
{
tools: {
profile: "coding",
allow: ["group:ui"]
}
}
If this is only for Steve, do it on the agent override instead:
{
agents: {
list: [
{
id: "steve",
tools: {
profile: "coding",
allow: ["browser"]
}
}
]
}
}
Also check this
If you use a restrictive plugins.allow, it must include browser too:
{
plugins: {
allow: ["browser"]
}
}
After changing it
- restart the gateway
- start a new Steve session
Otherwise he may keep the old tool exposure.
Docs:
Glad you found the core browser transport, this last bit is just tool policy.
After adding browser to tools.allow and restarting the gateway, the agent now lists browser in its available tools but when asked to actually use it says "browser execution isn't actually available to me in this session runtime." What's the difference between the tool appearing in the list and it actually being executable? Is there a separate step to make browser callable in agent sessions?
Yes, if Steve is running as an ACP / ACPX session, there is a separate step.
The missing piece is probably:
ACPX sessions do not expose OpenClaw plugin tools by default
That includes bundled plugin tools like browser.
So you can end up in this weird state where:
- the gateway/plugin/tool policy says
browserexists - the session can list it conceptually
- but the ACP session runtime still cannot actually call it
Fix
Enable the ACP plugin-tools bridge:
openclaw config set plugins.entries.acpx.config.pluginToolsMcpBridge true
Then:
- restart the gateway
- start a brand new Steve session
Why this happens
There are two layers:
- Gateway tool exposure / policy
- plugin enabled
tools.allowincludesbrowser
- ACP session runtime tool surface
- by default, ACPX does not forward plugin-registered OpenClaw tools into the harness runtime
pluginToolsMcpBridge: trueis what bridges them in
So âappears in the listâ and âactually executable in this session runtimeâ are different checks.
Docs:
- https://docs.openclaw.ai/tools/acp-agents#plugin-tools-mcp-bridge
- https://docs.openclaw.ai/tools/browser
If Steve is not ACP-backed, say which runtime heâs using and Iâll narrow it further, but for that exact wording, ACP bridge is the thing Iâd check first.
Followed both steps â enabled acpx plugin and set pluginToolsMcpBridge: true, restarted gateway, started fresh session. Steve still says "browser side is not connected in this runtime." Is Steve definitely running as an ACPX session? How do we confirm which runtime he's actually using, and what's the next step if it's not ACPX?
Yep, first confirm the runtime instead of guessing.
1) How to confirm what Steve is running on
In Steveâs session, run:
/acp status
What you want to see is a real ACP status card with:
- backend
- mode
- state
- runtime options
- capabilities
If it shows backend: acpx, then Steve is ACPX-backed.
If /acp status says thereâs no ACP session bound, or it doesnât resolve to an ACP runtime, then Steve is not ACPX, and pluginToolsMcpBridge wonât help.
Also useful:
/status
That gives the general session/runtime status.
2) If Steve is ACP, check which backend
This matters because the bridge you enabled is specifically for ACPX.
So:
backend: acpxâ the ACPX plugin bridge setting is relevant- some other ACP backend â that setting is not the fix
3) If Steve is not ACPX
Then the next thing Iâd check is sandbox/browser routing, not ACP.
For normal OpenClaw sessions, the browser tool can appear in policy but still fail at call time if the session is sandboxed and host browser control is blocked.
Relevant setting:
{
agents: {
defaults: {
sandbox: {
browser: {
allowHostControl: true
}
}
}
}
}
Or disable sandbox for Steve if that fits your setup better.
Then start a new Steve session again.
4) The exact thing to paste back
If you want the fastest next step, paste the output of:
/acp status/status
The key question is just:
does Steve show
backend: acpxor not?
Docs:
- https://docs.openclaw.ai/tools/acp-agents
- https://docs.openclaw.ai/tools/browser-login#sandboxing-host-browser-access
If /acp status shows a backend, I can tell you exactly what the next fix is from that.
/acp status shows Runtime: direct â Steve is not ACPX. So the pluginToolsMcpBridge fix wasn't relevant. Based on your earlier advice about sandbox browser routing, should we add agents.defaults.sandbox.browser.allowHostControl: true to the config? Or is there a different fix for direct runtime sessions that can't access the browser tool?
@zinc mango
Yep, Iâm here đŚ
Next step is still the runtime check. Please paste these from Steveâs session:
/acp status
/status
That will tell us whether Steve is actually:
- ACPX-backed
- another ACP backend
- or just a normal OpenClaw session with sandbox/browser routing issues
Once we have those two outputs, I can tell you the exact next fix instead of guessing.
Already ran both. /acp status output: "Runtime: direct ¡ Think: off ¡ Text: low ¡ Activation: mention ¡ Queue: collect (depth 0)" â Steve is NOT ACPX, runtime is direct. What's the fix for direct runtime sessions that can't access the browser tool?
@zinc mango
/acp status shows Runtime: direct â Steve is not ACPX. So the pluginToolsMcpBridge fix wasn't relevant. Based on your earlier advice about sandbox browser routing, should we add agents.defaults.sandbox.browser.allowHostControl: true to the config? Or is there a different fix for direct runtime sessions that can't access the browser tool?
Already ran both. /acp status output: "Runtime: direct ¡ Think: off ¡ Text: low ¡ Activation: mention ¡ Queue: collect (depth 0)" â Steve is NOT ACPX, runtime is direct. What's the fix for direct runtime sessions that can't access the browser tool?
Perfect, that narrows it down a lot.
Since Steve is Runtime: direct, there is no separate ACPX bridge step.
For direct sessions, the problem is usually one of these:
1) Steve is sandboxed
In direct sessions:
- sandboxed session â browser defaults to
target: "sandbox" - non-sandboxed session â browser defaults to
target: "host"
So if Steve is sandboxed and your usable browser lives outside the sandbox, then yes, this is the relevant fix:
{
agents: {
defaults: {
sandbox: {
browser: {
allowHostControl: true
}
}
}
}
}
Then restart the gateway and start a new Steve session.
2) Your split-host setup may be auto-routing to the node
This is the other big one.
Docs say that if a browser-capable node is connected, the browser tool may auto-route to the node unless you pin the target.
That matters here because:
- your Windows node is currently unhealthy / hanging
- your gateway-side remote CDP path already worked for
tabs
So Steve may be seeing browser, but trying to use the node-side browser runtime, which is exactly the broken part.
What Iâd do next
First test the known-good path explicitly from the gateway side:
openclaw browser --browser-profile brave tabs --target host
openclaw browser --browser-profile brave snapshot --target host
If that works, then the fix for Steve is:
- allow host control if Steve is sandboxed
- make Steve use
target: "host" - keep using the gateway-host remote CDP profile, not the broken Windows node browser side
Short version
For direct runtime there is no extra bridge.
The likely fix is:
agents.defaults.sandbox.browser.allowHostControl: trueif Steve is sandboxed- use/pin
target: "host" - start a new session
- ignore the Windows node browser path until the node hang is fixed
Docs:
- https://docs.openclaw.ai/tools/browser#agent-tools-how-control-works
- https://docs.openclaw.ai/tools/browser-login#sandboxing-host-browser-access
If this fixes it, please right-click the solution message â Apps â â Mark Solution so Answer Overflow can index it.
Still broken after all fixes. Summary of what we've done: added browser to tools.allow, enabled acpx plugin with pluginToolsMcpBridge: true, added sandbox.browser.allowHostControl: true, set gateway.nodes.browser.mode: off, restarted gateway after each change, started fresh sessions each time. Steve is Runtime: direct, not sandboxed, not ACPX. Every session Steve says browser capability isn't available. What are we missing?
I think weâve crossed from config problem into agent/tool-calling problem.
Because for a direct, non-sandboxed session:
- there is no ACPX bridge
allowHostControlis irrelevant- browser should default to
target: "host"
So the only config item you still havenât explicitly listed is:
1) plugins.allow
If you have a restrictive plugin allowlist on the WSL2 gateway, it must include browser:
{
plugins: {
allow: ["browser", "...other plugins..."]
}
}
or remove plugins.allow entirely if you donât need it.
And this is important:
tools.allowdoes not load the browser plugin.
It only changes policy after the plugin is loaded.
2) Stop trusting Steveâs prose, check whether he actually called the tool
That phrase:
âbrowser capability isn't availableâ
does not look like a native OpenClaw browser/runtime error string. It looks like Steve talking, not the runtime.
So the real check is:
In WSL2 gateway:
openclaw browser --browser-profile brave status --target host
openclaw browser --browser-profile brave tabs --target host
openclaw logs --follow
Then ask Steve, in a fresh session:
Use the
browsertool now. Callbrowser statuswithtarget: "host"andprofile: "brave". Do not explain first.
What to look for in logs
- If you see
browser.request, the tool was actually called - If you do not see
browser.request, Steve never called the tool at all
That distinction matters a lot.
3) What this likely means
If:
openclaw browser ... --target hostworks from WSL2, and- Steve still only says âbrowser isnât availableâ
then the browser runtime is fine, and the issue is:
Steve is refusing/failing to emit the tool call, not that browser is unavailable.
At that point, please paste:
- the exact result of
openclaw browser --browser-profile brave status --target host - whether
browser.requestappears inopenclaw logs --follow
Thatâll tell us immediately whether this is still config, or now an actual agent bug.
Docs:
- https://docs.openclaw.ai/tools/browser#missing-browser-command-or-tool
- https://docs.openclaw.ai/gateway/troubleshooting
If this fixed it, please right-click the solution message â Apps â â Mark Solution so Answer Overflow indexes it.
Steve is reporting capabilities=none in his session runtime. agents.defaults.sandbox only has browser.allowHostControl: true but no mode set. Is the missing sandbox mode causing capabilities to be stripped? Should we set sandbox.mode: "off" for Steve's agent?
No, thatâs not it.
Short answer
capabilities=nonein/acp statusis ACP runtime-control capability info- Steve is
Runtime: direct, so that field is basically irrelevant to browser tool access - Missing
sandbox.modeis not what strips browser - Setting
sandbox.mode: "off"for Steve is not the fix if Steve is already non-sandboxed
Also:
browser.allowHostControlonly matters when sandboxing is actually on
So if thereâs no sandbox mode active, that setting just sits there harmlessly.
What still matters
For a direct, non-sandboxed session, the real checks are still:
-
Does the gateway actually have the browser plugin loaded?
Ifplugins.allowexists, it must includebrowser. -
Can the gateway itself use the browser profile?
From WSL2:openclaw browser --browser-profile brave status --target host openclaw browser --browser-profile brave tabs --target host -
When Steve tries, does he actually emit a browser tool call?
Watch:openclaw logs --follow
Then ask Steve to use browser.
- If you see
browser.request, it called the tool - If you donât see
browser.request, Steve never called it
So the answer to your specific question
- No, missing
sandbox.modeis not causingcapabilities=none - No,
sandbox.mode: "off"is probably not the missing fix here
At this point the likely missing piece is still either:
plugins.allowdoes not includebrowser, or- Steve is not actually calling the tool, even though it appears in the tool list
If you paste:
- whether
plugins.allowis set - output of
openclaw browser --browser-profile brave status --target host
- whether
browser.requestappears in logs
we can pin it down fast.
@zinc mango
Full status for Krill:
plugins.allow â NOT SET. Confirmed: openclaw config get plugins.allow returns "Config path not found"
openclaw browser --browser-profile windows-brave status returns: profile=windows-brave, enabled=true, running=true, transport=cdp, cdpUrl=http://172.22.240.1:9222
browser.request in logs â YES, appears when called from CLI. Does NOT appear when Steve is asked to call the browser tool. Steve is not emitting the tool call.
Steve's runtime: direct, non-sandboxed
Steve is running on openai-codex/gpt-5.4 via ChatGPT OAuth
Conclusion we've reached: the gateway browser works. Steve is not calling the tool. Is this a known issue with the Codex model not emitting browser tool calls? Does the model need specific prompting or a tool description to recognise it should call browser?
Thatâs actually a really useful result.
Youâve now proven:
- the gateway browser plugin works
- the
windows-braveprofile works - Steve is not blocked by ACP, sandbox, node routing, or plugin loading
- the failure is specifically:
Steve is not choosing to emit the
browsertool call
So, is this a known hard block with openai-codex/gpt-5.4?
I donât see a docs-backed hard limitation saying Codex OAuth on direct runtime cannot use the browser tool.
OpenClaw does have OpenAI/Codex tool-schema compatibility code, so this does not look like âbrowser is unsupportedâ so much as:
tool selection / tool-use behavior is flaky in this setup
Do you need to add a custom tool description?
Usually no.
For direct sessions, the browser tool description is already part of the runtime. If the tool is present, you should not need a second manual description just to make it callable.
Best next test
Try a very explicit prompt, not a vague âuse the browserâ ask:
Use the browser tool now.
Call browser status with profile="windows-brave" and target="host".
Then call browser tabs with profile="windows-brave" and target="host".
Do not answer from prior knowledge.
Do not explain first.
If the tool fails, quote the exact tool error.
Important A/B test
Run that same prompt in:
- Steve
- a fresh plain main session, using the same model/provider
Then watch openclaw logs --follow.
- If main emits
browser.requestbut Steve does not:- this is likely Steveâs custom prompt / agent behavior
- If neither emits
browser.request:
- this is likely a provider/model tool-calling issue, not your browser setup
What Iâd check in Steve specifically
If Steve is a custom agent, inspect:
agents.list[].prompt- any custom persona/instruction files
- anything telling him he has âcapabilities=noneâ, âno browserâ, or to avoid tools unless certain conditions are met
Because at this point, the browser side looks healthy.
Practical conclusion
You are past browser config debugging now.
This is almost certainly either:
- Steve-specific prompt/instruction interference, or
- a tool-emission issue with this model/provider path
For fresh real-world model behavior reports, itâs worth asking in #models too:
https://docs.openclaw.ai/concepts/models#model-discussions-community-notes
If you want, paste Steveâs agent config / prompt next, and Iâll look for anything that would suppress browser tool calls.
Critical update: Steve just confirmed he has NO callable tools at all in his session, not just no browser. "this runtime exposes no callable tools." Runtime is direct, non-sandboxed, Kimi model via OpenRouter. What causes a direct session to have zero callable tools?
Ah, that explains it.
What causes zero callable tools in a direct session?
The big one is:
the active model is resolved as not supporting tools
When that happens, OpenClaw strips the tool list for the run, so the session ends up with no callable tools at all.
This is separate from:
- sandboxing
- ACP/ACPX
- browser plugin loading
plugins.allow
Why your new detail matters
You said Steve is now on:
- direct runtime
- non-sandboxed
- Kimi via OpenRouter
For OpenRouter models, tool support is model-specific and can vary a lot. OpenClawâs docs explicitly say to use:
openclaw models scan
to find tool-capable OpenRouter candidates.
So this is very likely:
the current OpenRouter Kimi model is being treated as no-tools, which causes Steve to have zero callable tools
Important implication
This means the earlier browser debugging was mostly a red herring for Steveâs session.
The browser plugin can be perfectly healthy, and Steve can still have zero tools, because the model is whatâs gating tool use.
What to do next
-
Run:
openclaw models scanand look for a tool-capable candidate on your OpenRouter setup.
-
Switch Steve to a model that actually supports tools.
- If you need real-world current recommendations, ask in #models:
https://docs.openclaw.ai/concepts/models#model-discussions-community-notes
If you want to prove it
Try the same Steve session on a known tool-capable model.
If tools suddenly appear, that confirms it was model capability, not browser config.
Docs:
- https://docs.openclaw.ai/concepts/models#scanning-openrouter-free-models
- https://docs.openclaw.ai/cli/models
If this fixed it, please right-click the solution message â Apps â â Mark Solution so Answer Overflow can index it.
@zinc mango openclaw models scan returns "No tool-capable OpenRouter free models found." The scan only covers free models. Steve needs a tool-capable model. What's the recommended tool-capable OpenRouter model to use that won't break the bank?
I wouldnât hardcode a specific OpenRouter model recommendation here, that changes too fast and Iâll just age badly.
For current cheap-but-tool-capable picks, the right place is #models:
https://docs.openclaw.ai/concepts/models#model-discussions-community-notes
What I can say confidently:
- free OpenRouter models are a bad fit for tool-heavy agents
- for browser/tool reliability, a first-party provider path is usually more predictable than an aggregator pass-through
- a good practical setup is:
- cheap chat model for normal convo
- strong tool-capable model only for Steve / browser / file / exec work
So Iâd ask in #models for:
âlowest-cost OpenRouter model people are currently using successfully for OpenClaw tool calling / browser toolâ
Thatâll get you fresher answers than anything I can safely guess here.
If you want, I can help you wire Steve so only that one agent uses the pricier tool-capable model, while everything else stays cheap.
Browser profile windows-brave is confirmed working from CLI (openclaw browser --browser-profile windows-brave status returns running=true). But Steve's sessions cannot see it â he says "Browser profile windows-brave is not currently available in this tool context." Steve is now on openai-codex/gpt-5.4, Runtime: direct, non-sandboxed. What makes a browser profile visible to agent sessions vs only visible from CLI?
There isnât a second âagent-visible browser profilesâ registry.
If all of these are true on the same gateway:
- browser plugin is loaded
- Steve has the
browsertool openclaw browser --browser-profile windows-brave statusworks
then windows-brave is available to agent tool calls too.
So whatâs the difference?
The difference is usually just this:
CLI knows the profile name because you gave it explicitly.
The agent does not automatically get a pre-listed catalog of your custom profile names.
So Steve may be guessing that windows-brave is unavailable instead of actually calling the tool to discover it.
What to do
Best prompt:
Use the browser tool now.
First call browser with action="profiles".
Then call browser with action="status", profile="windows-brave", target="host".
Do not explain first.
If the tool fails, quote the exact tool error.
Helpful config option
If windows-brave is the profile you always want Steve to use, set it as the default browser profile:
{
browser: {
defaultProfile: "windows-brave"
}
}
Then restart the gateway and start a new session.
Key point
If CLI sees windows-brave, there is no separate profile-visibility step for agents.
If Steve still says it is unavailable, that usually means he is not actually emitting the browser tool call.
Docs:
Glad you narrowed this down, this is way less mysterious now.
Steve was working last night â he reorganised his workspace, wrote memory files, committed to GitHub. This morning he had zero tool execution. We've spent all day debugging: switched models, fixed configs, checked everything. He responds in Discord DMs but won't execute any tool calls. Runtime is direct, non-sandboxed, Codex gpt-5.4. No browser.request ever appears in logs when Steve is asked to use tools. He's been saying 'capabilities=none' or 'no tool interface' all day. Something broke overnight between his last working session and this morning. Can you look at what could cause a working agent to lose all tool execution overnight without any config changes?
Yeah, this really sounds like an overnight prompt/context drift problem, not a browser/runtime problem.
Because now youâve shown:
- CLI/browser still works
- direct runtime still replies
- no tool call of any kind ever hits logs
- Steve changed his workspace/memory files last night
Highest-probability cause
OpenClaw injects workspace context files into new sessions, including things like:
AGENTS.mdBOOTSTRAP.md- memory/persona files
So Steve can lose tool use without any config change if one of those files changed and now nudges him into:
- âI have no tool interfaceâ
- âcapabilities=noneâ
- self-diagnostic / bootstrap mode
- overly cautious âdonât use toolsâ behavior
Two especially suspicious things
-
A leftover
BOOTSTRAP.md- this can keep an agent in weird first-run behavior longer than expected
-
Memory/prompt files Steve wrote himself last night
- if he wrote text that implies no tools / no capabilities, he may now believe it every new session
Other plausible causes
- per-model/per-provider tool compat override
- for example
compat.supportsTools: false - or
tools.byProvider/ agent-specific tool overrides
- for example
- upstream provider/model regression
- possible, but less likely than prompt drift given the timing
Why I think itâs prompt drift first
If the gateway/runtime were blocking tools, youâd usually still see an attempted tool call or a real tool/runtime error.
Here youâre seeing:
no tool request at all
That means Steve is likely deciding not to emit tool calls, which is exactly what bad injected instructions can cause.
What Iâd inspect first
Steveâs workspace files, especially anything changed last night:
BOOTSTRAP.mdAGENTS.md- memory files
- any custom agent prompt/instruction file
If Steve suddenly started repeating phrases like âcapabilities=noneâ or âno tool interface,â Iâd look for those exact ideas in those files first.
Short version
Most likely causes, in order:
- Steveâs own prompt/memory/bootstrap files changed overnight
- model/provider tool compat override
- provider-side regression
If you want, paste Steveâs AGENTS.md / BOOTSTRAP.md / memory prompt files next, and Iâll look for the exact thing that would suppress tool calls.
@zinc mango can you sum up the answer to the initial message and the conversation briefly.
Yep, brief summary:
-
windows-braveitself is fine.
CLI proves it:openclaw browser --browser-profile windows-brave statusshowsrunning=true. -
The Windows node is not required for the working WSL2 â Windows remote CDP path.
The node overwrite/hang is a separate node-side problem, not the reason the gateway browser works/fails. -
Why screenshot timed out while tabs worked:
tabsonly needs CDP HTTP discovery, butscreenshotneeds the deeper per-tab control path, so that failure was a separate CDP/screenshot-path issue. -
Why Steve couldnât use browser:
the browser plugin/tool path was mostly okay, but Steve often wasnât emitting tool calls at all. Nobrowser.requestin logs means the model/agent never actually called the tool. -
Tool/profile visibility:
there is no separate âCLI-only vs agent-visibleâ browser profile registry.
If CLI can usewindows-brave, agent sessions can too, provided the agent actually emits the browser tool call.
-
Big debugging conclusion:
the later issue stopped being âbrowser configâ and became either:- model/tool capability (for example OpenRouter/Kimi ended up with zero callable tools), or
- agent prompt/context drift (Steve changed workspace/memory/bootstrap files overnight and may have talked himself into âno tools / capabilities=noneâ behavior).
-
Most likely current explanation for Steve losing all tools overnight without config changes:
prompt/context files changed, or the active model path resolved to no tools.
If this fixed it, please right-click the solution message â Apps â â Mark Solution so Answer Overflow can index it.
Why do cron isolated sessions work but Discord group sessions don't execute tool calls
Because theyâre different execution contexts, not the same session wearing a different hat.
In practice, cron isolated sessions often work when Discord group sessions donât because cron runs with a much cleaner stack:
- fresh isolated session
- often a different model override
- its own tool policy / sandbox resolution
- less group-chat/social prompt baggage
- less chance of bad workspace memory/bootstrap drift
Discord group sessions, by contrast, inherit more context:
- group/channel behavior
- agent workspace files (
AGENTS.md, memory,BOOTSTRAP.md, etc.) - any session-specific model/tool overrides
- non-main/session-key behavior
So if:
- cron isolated sessions can call tools, but
- Discord group sessions never emit tool calls
then the important conclusion is:
the tool runtime itself is fine
and the break is in the Discord sessionâs specific prompt/model/policy/context
Not âDiscord canât use toolsâ, but âthis Discord-bound agent/session is deciding or being configured not to.â
Thatâs why we kept narrowing back to:
- model/tool capability
- agent prompt/workspace drift
- session-specific policy/override differences
rather than browser/plugin failure.
Steve lists all tools including exec, browser, grep etc. But when asked to run any command says 'I can't run commands from this turn.' Cron isolated sessions work perfectly. Interactive sessions (Discord, control panel) cannot execute any tools. What causes 'can't run commands from this turn' in interactive sessions when tools are listed as available?
That wording is the giveaway.
âI canât run commands from this turnâ is not a native OpenClaw tool/runtime error.
Itâs the model refusing, even though the tools are policy-visible.
So if:
- tools are listed
- cron isolated sessions execute them fine
- interactive sessions never emit tool calls
then the cause is usually:
1) prompt/rule differences in interactive sessions
The agent is being told, implicitly or explicitly, not to act in chat turns.
Typical culprits:
AGENTS.mdBOOTSTRAP.md- memory / persona files
- âread-only in public chatsâ
- âdonât run commands interactivelyâ
- âonly advise, donât executeâ
2) session-context differences
Interactive Discord / Control UI sessions carry more user/chat context, and the model may become over-cautious there.
3) listed tools != guaranteed execution
The tool list is just what policy allows.
The model can still decide not to call them.
Why cron works
Cron isolated sessions are usually cleaner:
- less chat/social context
- fewer cautionary instructions
- more task-oriented prompt setup
Short answer
If cron works but interactive sessions say âI canât run commands from this turn,â the problem is almost certainly agent prompt/context drift in interactive sessions, not broken tools.
Iâd inspect Steveâs workspace prompt files for anything that tells him to stay read-only or not execute in chat turns.
@zinc mango We enabled the acpx plugin (plugins.entries.acpx.enabled: true) during debugging, then disabled it again. Since then, interactive sessions (Discord and control panel) show tools in the list but the model never emits tool calls â it just describes what it would do. Cron isolated sessions work fine. Could enabling then disabling acpx have left residual state that affects interactive session tool calling? How do we fully reset to pre-acpx state?
Short answer: probably not.
There isnât a docs-backed path where simply enabling then disabling acpx should poison direct sessions globally once the gateway is restarted.
For direct runtime sessions:
- ACPX is not the tool bridge
- disabling
plugins.entries.acpx.enabledshould take ACPX out of play - cron isolated sessions still working is another sign the core tool runtime is fine
What can linger
The thing that can persist is ACP session state/bindings/options on conversations that were previously ACP-bound.
If you want a full ACP cleanup, do all of these:
- keep:
plugins.entries.acpx.enabled: false
- if you added any ACPX-specific config, remove/reset it
- run in affected conversations:
/acp close /acp reset-options - start a brand new session/thread
- restart the gateway
If you also added acpx to a restrictive plugins.allow, remove it there too if you want to get back to the exact old shape.
Important nuance
If /acp status now says Runtime: direct, then ACP residue is less likely to be the real cause.
At that point, the more likely causes are still:
- interactive-session prompt/context drift
- agent workspace files
- session-specific model/tool behavior
So yes, you can do the ACP cleanup above, but I would not treat ACPX residue as the main suspect unless /acp status still shows ACP bindings.
If this fixed it, please right-click the solution message â Apps â â Mark Solution so Answer Overflow can index it.
Steve on Codex gpt-5.4 in interactive sessions keeps narrating actions instead of executing them. Cron isolated sessions work fine. Is this a known Codex interactive session behaviour issue and is there a config fix?
I donât know of a docs-backed âCodex interactive sessions narrate instead of executingâ bug with a one-line config fix, and I wouldnât want to make one up.
What I can say:
- there is no magic config that forces interactive direct sessions to stop narrating and start emitting tool calls
- if cron isolated sessions work, the core tool runtime is probably fine
- that points much more to interactive-session context/prompt behavior than a global Codex setting
So this is more likely:
- interactive prompt/workspace drift
- session-specific behavior
- model/provider tool-selection weirdness
not âbrowser/tool system is brokenâ.
Important distinction
openai-codex/gpt-5.4 in a direct interactive session is still normal model tool-calling.
It is not the same thing as a Codex harness / ACP coding session.
If you want Codex-style execution behavior rather than plain model tool selection, the more appropriate path is the Codex ACP/harness flow, not the direct interactive model path.
For current real-world model behavior reports, ask in #models:
https://docs.openclaw.ai/concepts/models#model-discussions-community-notes
For harness-style setup docs:
So, short answer:
Not a known âflip this config and direct Codex interactive stops narratingâ issue.
The likely fix is session/prompt cleanup or switching that workflow to a harness path.
@zinc mango Steve says 'Runner access is disabled in this session' in every interactive Discord session. This is a specific error message. What causes runner access to be disabled in a session and how do we enable it?
I canât find âRunner access is disabled in this sessionâ as a native OpenClaw error string, so I donât think thereâs a literal runnerAccess: false switch to flip.
What is relevant for direct openai-codex/gpt-5.4 interactive sessions is the embedded execution contract.
Best config to try
{
agents: {
defaults: {
embeddedPi: {
executionContract: "strict-agentic"
}
}
}
}
Or just for Steve:
{
agents: {
list: [
{
id: "steve",
embeddedPi: {
executionContract: "strict-agentic"
}
}
]
}
}
Then:
- restart the gateway
- start a new session
Why this is the closest fix
For GPT-5 / Codex-family direct runs, strict-agentic is the setting that tells OpenClaw:
- donât accept plan-only / narration-only turns
- push the model to act when a tool is available
- surface a real blocked state instead of âI would do Xâ
So it doesnât âenable runner accessâ exactly, but it does address the behavior youâre seeing.
Docs:
If that still doesnât change the behavior, Iâd treat the phrase as model/prompt-side narration, not a real runner-state error.