#Windows node hung on startup + config overwrite removing browser profiles

1 messages ¡ Page 1 of 1 (latest)

fallow perch
#

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?

midnight yokeBOT
warped radishBOT
#

To help others find answers, you can mark your question as solved via Right click solution message -> Apps -> ✅ Mark Solution

zinc mangoBOT
#

You’ve got two separate things mixed together here.

  1. The brave profile persistence issue
  2. 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:

  • navigate
  • act
  • AI / role snapshots
  • element screenshots
  • PDF
#

Docs:

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 brave in 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.

fallow perch
#

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?

zinc mangoBOT
#

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, and brave
  • so profiles=2 means cfg.browser.profiles.brave was missing by the time it was loaded
  • also, 127.0.0.1:9222 is not used by driver: "existing-session" anyway, that port matters for raw remote CDP, not Chrome MCP existing-session attach

Most likely causes now

  1. Wrong config file / wrong user / env override
    • OPENCLAW_CONFIG_PATH
    • OPENCLAW_STATE_DIR
    • running under a different Windows user/profile than the file you edited
#
  1. The full JSON file is malformed

    • your pasted snippet starts with json"browser"...
    • if json is 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 browser block itself is fine
  2. You’re reading the node host banner as startup

    • openclaw node run normally sits in the foreground and waits
    • Browser control service ready is 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 profiles shows only openclaw + user, then it’s definitely a config path / user / parse problem on Windows
  • if it shows brave locally, 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 cdpUrl profile on WSL2, not existing-session

Docs:

fallow perch
#

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?

zinc mangoBOT
#

The new clue is this:

openclaw browser --browser-profile brave tabs from 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

  1. profiles=2 is a red herring

    • resolveBrowserConfig() always injects openclaw + user
    • if you were really running against the Windows brave config, you’d expect 3
    • but since tabs with --browser-profile brave works, 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
  2. openclaw browser profiles on 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 45000ms just means that proxied request stalled, not that the Windows config file was definitely unreadable
#
  1. 9222 is not part of existing-session
    • driver: "existing-session" uses Chrome MCP attach
    • 127.0.0.1:9222 matters for raw remote CDP
    • so don’t use the remote-debugging port as proof that the existing-session path 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 profiles and screenshot

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 tabs working means the profile is probably not the thing that’s missing
  • the Windows profiles timeout is a gateway/proxy timeout, not a clean local config check
  • 9222 is irrelevant for existing-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.

fallow perch
#

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.

zinc mangoBOT
#

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, not 127.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:

#

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.

fallow perch
#

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.

zinc mangoBOT
#

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

  1. Fully quit Brave
  2. Start Brave with remote debugging enabled
  3. On Windows, verify:
curl http://127.0.0.1:9222/json/version
curl http://127.0.0.1:9222/json/list
  1. 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.0 is 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:

  1. Keep the Windows node-host + existing-session path
    No 9222 exposure needed, but you’re dealing with the Chrome MCP timeout path.

  2. Run the Gateway on Windows instead of WSL2
    Then existing-session stays host-local.

  3. 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.

fallow perch
#

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?

zinc mangoBOT
#

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/version returns JSON with fields like Browser, Protocol-Version, webSocketDebuggerUrl
  • /json/list returns 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:

  1. Windows curl 127.0.0.1:9222
  2. WSL2 curl 172.22.240.1:9222
  3. WSL2 openclaw browser tabs --browser-profile windows-brave
fallow perch
#

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?

zinc mangoBOT
#

The reason is that tabs and screenshot are exercising different parts of CDP.

  • tabs only needs the HTTP discovery endpoints like /json/list
  • screenshot needs the per-tab DevTools WebSocket and then a live Page.captureScreenshot call

So this combination:

  • curl http://172.22.240.1:9222/json/version works
  • openclaw browser tabs --browser-profile windows-brave works
  • openclaw browser screenshot --browser-profile windows-brave times 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

  1. The bundled CLI you ran installs the Chromium browser binary, not the full playwright npm package.
    So that install does not explain or fix this timeout.

  2. 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:

fallow perch
#

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?

zinc mangoBOT
#

At this point, the pattern is pretty clear:

  • /json/version works
  • /json/list works
  • tab listing works
  • every tab has a valid webSocketDebuggerUrl
  • only screenshot fails, either with abnormal closure, no close reason or 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

  1. 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
#
  1. 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
  2. If it reproduces consistently, file a GitHub issue
    Include:

    • openclaw --version (2026.4.11)
    • Windows 11 + WSL2 Ubuntu 24.04
    • curl /json/version works
    • curl /json/list works
    • 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.

fallow perch
#

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?

zinc mangoBOT
#

Yep, this one is simpler.

The key bit is:

tools.profile: "coding" does not include browser

The coding profile includes:

  • group:fs
  • group:runtime
  • group:web
  • group:sessions
  • group: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:

fallow perch
#

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?

zinc mangoBOT
#

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 browser exists
  • 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:

  1. restart the gateway
  2. start a brand new Steve session

Why this happens

There are two layers:

  1. Gateway tool exposure / policy
    • plugin enabled
    • tools.allow includes browser
#
  1. ACP session runtime tool surface
    • by default, ACPX does not forward plugin-registered OpenClaw tools into the harness runtime
    • pluginToolsMcpBridge: true is what bridges them in

So “appears in the list” and “actually executable in this session runtime” are different checks.

Docs:

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.

fallow perch
#

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?

zinc mangoBOT
#

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:

fallow perch
#

/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

zinc mangoBOT
#

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.

fallow perch
#

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?

zinc mangoBOT
#

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:

  1. agents.defaults.sandbox.browser.allowHostControl: true if Steve is sandboxed
  2. use/pin target: "host"
  3. start a new session
  4. ignore the Windows node browser path until the node hang is fixed

Docs:

fallow perch
#

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?

zinc mangoBOT
#

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
  • allowHostControl is 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.allow does 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 browser tool now. Call browser status with target: "host" and profile: "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 host works 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.request appears in openclaw logs --follow

That’ll tell us immediately whether this is still config, or now an actual agent bug.

Docs:

If this fixed it, please right-click the solution message → Apps → ✅ Mark Solution so Answer Overflow indexes it.

fallow perch
#

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?

zinc mangoBOT
#

No, that’s not it.

Short answer

  • capabilities=none in /acp status is ACP runtime-control capability info
  • Steve is Runtime: direct, so that field is basically irrelevant to browser tool access
  • Missing sandbox.mode is not what strips browser
  • Setting sandbox.mode: "off" for Steve is not the fix if Steve is already non-sandboxed

Also:

browser.allowHostControl only 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:

  1. Does the gateway actually have the browser plugin loaded?
    If plugins.allow exists, it must include browser.

  2. 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
    
  3. 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.mode is not causing capabilities=none
  • No, sandbox.mode: "off" is probably not the missing fix here

At this point the likely missing piece is still either:

  • plugins.allow does not include browser, or
  • Steve is not actually calling the tool, even though it appears in the tool list

If you paste:

  • whether plugins.allow is set
  • output of openclaw browser --browser-profile brave status --target host
#
  • whether browser.request appears in logs

we can pin it down fast.

fallow perch
#

@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?

zinc mangoBOT
#

That’s actually a really useful result.

You’ve now proven:

  • the gateway browser plugin works
  • the windows-brave profile works
  • Steve is not blocked by ACP, sandbox, node routing, or plugin loading
  • the failure is specifically:

Steve is not choosing to emit the browser tool 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:

  1. Steve
  2. a fresh plain main session, using the same model/provider

Then watch openclaw logs --follow.

  • If main emits browser.request but 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:

#
  1. Steve-specific prompt/instruction interference, or
  2. 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.

fallow perch
#

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?

zinc mangoBOT
#

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

  1. Run:

    openclaw models scan
    

    and look for a tool-capable candidate on your OpenRouter setup.

  2. Switch Steve to a model that actually supports tools.

#
  1. 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:

If this fixed it, please right-click the solution message → Apps → ✅ Mark Solution so Answer Overflow can index it.

fallow perch
#

@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?

zinc mangoBOT
#

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.

fallow perch
#

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?

zinc mangoBOT
#

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 browser tool
  • openclaw browser --browser-profile windows-brave status works

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:

fallow perch
#

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?

zinc mangoBOT
#

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.md
  • BOOTSTRAP.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

  1. A leftover BOOTSTRAP.md

    • this can keep an agent in weird first-run behavior longer than expected
  2. 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
  • 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.md
  • AGENTS.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:

  1. Steve’s own prompt/memory/bootstrap files changed overnight
  2. model/provider tool compat override
  3. 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.

midnight yokeBOT
fallow perch
#

@zinc mango can you sum up the answer to the initial message and the conversation briefly.

zinc mangoBOT
#

Yep, brief summary:

  • windows-brave itself is fine.
    CLI proves it: openclaw browser --browser-profile windows-brave status shows running=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:
    tabs only needs CDP HTTP discovery, but screenshot needs 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. No browser.request in 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 use windows-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:

    1. model/tool capability (for example OpenRouter/Kimi ended up with zero callable tools), or
    2. 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.

fallow perch
#

Why do cron isolated sessions work but Discord group sessions don't execute tool calls

zinc mangoBOT
#

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:

  1. model/tool capability
  2. agent prompt/workspace drift
  3. session-specific policy/override differences
#

rather than browser/plugin failure.

fallow perch
#

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?

zinc mangoBOT
#

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.md
  • BOOTSTRAP.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.

fallow perch
#

@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?

zinc mangoBOT
#

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.enabled should 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:

  1. keep:
#
   plugins.entries.acpx.enabled: false
  1. if you added any ACPX-specific config, remove/reset it
  2. run in affected conversations:
    /acp close
    /acp reset-options
    
  3. start a brand new session/thread
  4. 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.

fallow perch
#

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?

zinc mangoBOT
#

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:

  1. interactive prompt/workspace drift
  2. session-specific behavior
  3. 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.

fallow perch
#

@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?

zinc mangoBOT
#

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: