#Restrict Specific CLI Commands?
1 messages Ā· Page 1 of 1 (latest)
Yep.
Best way to do it is with three layers:
-
Per-agent tool policy
Denyexecentirely for agents that should never touch the machine. -
Exec allowlist
For agents that can run commands, set exec toallowlistmode and only allow specific binary paths, per agent. -
Sandboxing
Run those agents withsandbox.mode: "all"so allowed commands stay inside the sandbox unless you separately enable elevated exec.
Example:
{
agents: {
list: [
{
id: "restricted",
sandbox: { mode: "all", scope: "agent" },
tools: {
deny: ["exec", "process", "browser", "write", "edit", "apply_patch"]
}
}
]
},
tools: {
exec: {
security: "allowlist",
ask: "on-miss",
strictInlineEval: true
}
}
}
Then allow only specific CLIs for one agent:
openclaw approvals allowlist add --agent restricted "/usr/bin/uptime"
Important caveat: the built-in allowlist is mainly tool-level and binary-path-level, not a rich āthis agent can run git status but not git pushā policy for every CLI. If you need that level of isolation, Iād use a wrapper CLI or a separate sandbox/node/VM.
Docs:
- https://docs.openclaw.ai/tools/multi-agent-sandbox-tools
- https://docs.openclaw.ai/tools/exec-approvals
- https://docs.openclaw.ai/gateway/sandbox-vs-tool-policy-vs-elevated
- https://docs.openclaw.ai/gateway/configuration-reference
If you want, I can sketch the exact config for one āread-onlyā, one āsafe execā, and one āfull accessā agent setup.
If you do have the agent in a sandbox, though, all of those EXEC commands are run in that sandbox and there is no way for them to run CLIs outside of that sandbox. Is that correct?
Not as an absolute guarantee, no.
The accurate version is:
- By default, yes: if the session is sandboxed, normal
execwithhost=autoruns inside the sandbox. - But there are explicit escape paths:
- elevated exec can break out to the gateway or a node if enabled
- node-targeted exec is also outside the sandbox
- risky bind mounts can weaken the boundary a lot (
/var/run/docker.sockis the classic footgun)
So if you want āthis agent can never run host CLIs outside the sandboxā, Iād combine:
- sandbox enabled for that agent
tools.elevated.enabled: false(or per-agent disable)- no node exec for that agent
- tight tool policy, or just deny
execentirely - minimal bind mounts, preferably read-only, and never mount
docker.sock
Also handy for checking whatās actually effective:
openclaw sandbox explain --agent <agentId>
Docs:
- https://docs.openclaw.ai/gateway/sandbox-vs-tool-policy-vs-elevated
- https://docs.openclaw.ai/gateway/sandboxing
If you want, I can mock up a āsandbox-only, no host escapeā agent config.
Yeah, please mock up that config. I also am unsure what you mean by node exec.
Yep. And ānode execā is easy to overthink.
Node exec just means: run the command on a paired node device instead of the gateway machine.
Example: Gateway is on a VPS, but youāve paired your laptop as a node host. exec host=node would run the CLI on the laptop.
If you have not set up a node host / companion app for shell execution, you can mostly ignore that path.
1. Hard lock, no machine CLI access at all
If you want an agent to never run shell commands anywhere:
{
agents: {
list: [
{
id: "restricted",
workspace: "~/.openclaw/workspace-restricted",
sandbox: { mode: "all", scope: "agent" },
tools: {
allow: ["read", "web_search", "web_fetch", "session_status"],
deny: ["exec", "process", "write", "edit", "apply_patch", "browser"]
}
}
]
}
}
That is the safest option.
2. Sandboxed CLI only
If you want the agent to use CLI tools, but only inside the sandbox:
{
agents: {
list: [
{
id: "builder",
workspace: "~/.openclaw/workspace-builder",
sandbox: {
mode: "all",
scope: "agent",
workspaceAccess: "rw"
},
tools: {
allow: ["read", "write", "edit", "apply_patch", "exec", "process"],
elevated: { enabled: false },
exec: {
host: "sandbox"
}
}
}
]
}
}
Then keep these rules:
- do not enable elevated for that agent
- do not set
/exec host=node - keep sandbox binds tight
- never mount
/var/run/docker.sock
Important nuance
Inside a sandboxed agent, the agent can only use the CLIs that exist inside the sandbox container. So if you want to block git, docker, kubectl, etc., the cleanest move is to use a sandbox image that simply does not have them installed.
So the practical pattern is:
- deny
execif you want a hard guarantee - if you allow
exec, keep it sandboxed and keep the sandbox image minimal
Docs:
If you want, I can also mock up a āsafe builderā sandbox image idea, like āonly bash + rg + pnpm, no git/docker/kubectlā.
Yeah, I definitely want them to be able to use CLI tools, but only inside the sandbox. So essentially, well, do you actually need the allow list in your example? If I am fine with them executing any CLI that is installed in that sandbox, and then having elevated access as false?
And do you need the explicit exec host sandbox? Configuration there? Or is that implicit with them running in a sandbox?
Yep, if thatās your goal, you can simplify it a lot.
Short answer
- No, you do not need an exec allowlist if youāre happy with āanything installed inside the sandbox is fair game.ā
- No, you do not strictly need
exec.host: "sandbox"if the agent is already sandboxed. Normalexecwithhost=autowill resolve to the sandbox.
The important distinction
There are two different āallowlistsā people mix together:
-
tools.allow/tools.deny
This controls which OpenClaw tools the agent can call (exec,read,browser, etc.) -
exec approvals allowlist
This controls which host/node binaries can run when exec is targeting the gateway or a node
If you are keeping exec inside the sandbox, the second one is usually not relevant.
Minimal config for what you want
{
agents: {
list: [
{
id: "sandboxed-cli",
workspace: "~/.openclaw/workspace-sandboxed-cli",
sandbox: {
mode: "all",
scope: "agent",
workspaceAccess: "rw"
},
tools: {
elevated: { enabled: false }
}
}
]
}
}
Optional, but nice
You can still add this:
tools: {
exec: {
host: "sandbox"
}
}
or per-agent:
tools: {
elevated: { enabled: false },
exec: { host: "sandbox" }
}
That is not strictly required, but I like it because it makes the intent explicit and helps it fail closed if sandboxing is not active for some reason.
One nuance about nodes
host=auto does not automatically choose a node.
But if you deliberately set up node exec and explicitly target host=node, that is an outside-the-sandbox path.
So if you want the cleanest mental model:
- sandbox the agent
- keep
elevated: false - do not use node exec for that agent
- only install the CLIs you want inside the sandbox image
That gives you basically, āthe agent can run whatever is in the container, and nothing on the host.ā
If you want, I can mock up the next step, which is a minimal sandbox image with only a few allowed CLIs installed.
The sandbox all mode means that even sub agents run in that sandbox, right?
I also want the agents to be sandboxed, but still able to use a browser. Is that on by default or how does that work?
Yes, with one nuance.
sandbox.mode: "all" means sub-agents are sandboxed too.
If a sandboxed requester tries to spawn a target that would run unsandboxed, OpenClaw rejects it.
The nuance is scope:
scope: "session": each session/sub-agent gets its own sandboxscope: "agent": sessions for that agent share the same agent-scoped sandboxscope: "shared": broader shared sandbox scope
So:
- sandboxed, yes
- same exact sandbox instance, depends on
scope
If you want parent + sub-agents all sandboxed, but isolated from each other, use:
sandbox: {
mode: "all",
scope: "session"
}
If you want them all sandboxed but sharing the same environment for that agent, use scope: "agent" instead.
Yep, that works.
Short version
- Yes, sandboxed agents can still use a browser.
- In a sandboxed session, the browser tool defaults to the sandbox browser.
- The sandbox browser auto-starts by default when needed.
- For your setup, the normal path is: sandboxed agent + sandboxed browser.
What needs to be true
- Sandboxing is enabled
- Browser feature is enabled (
browser.enabledistrueby default) - The browser tool is not denied by tool policy
- if you use a restrictive
tools.allow, includebrowser(orgroup:ui)
- if you use a restrictive
Good baseline config
{
agents: {
list: [
{
id: "sandboxed-browser-agent",
workspace: "~/.openclaw/workspace-sandboxed-browser-agent",
sandbox: {
mode: "all",
scope: "session"
},
tools: {
elevated: { enabled: false },
allow: ["group:fs", "group:runtime", "browser", "session_status"]
}
}
]
}
}
If you are not using a restrictive tools.allow, you can omit that part.
One important caveat
If you want sandbox + browser, the easiest/normal backend is Docker.
The docs note that sandbox browser support is not there for every remote backend.
Host browser vs sandbox browser
By default, a sandboxed agent uses the sandbox browser.
If you ever want a sandboxed agent to control the host browser instead, that is a separate opt-in:
sandbox: {
browser: {
allowHostControl: true
}
}
But for your stated goal, you probably do not need that.
Docs:
Glad that helped! š
If this fixed it, please right-click the solution message ā Apps ā ā Mark Solution.
What do you suggest for a sandboxed browser within Docker? Is that included in the common setup script or should I have something else?
Iād use the built-in sandbox browser image first. Thatās the path the docs expect.
Recommendation
For a Docker-backed sandboxed browser:
- use the normal Docker sandbox backend
- enable
sandbox.browser.enabled: true - use the bundled browser image (
openclaw-sandbox-browser:bookworm-slim) - keep
autoStart: true
Important distinction
There are two setup scripts:
scripts/sandbox-common-setup.sh
builds the general CLI sandbox image
scripts/sandbox-browser-setup.sh
builds the browser sandbox image
So if you want a sandboxed browser, the common setup script is not the one.
You want the browser setup script.
Minimal config shape
{
agents: {
list: [
{
id: "sandboxed-browser-agent",
sandbox: {
mode: "all",
scope: "session",
browser: {
enabled: true,
autoStart: true
}
},
tools: {
elevated: { enabled: false },
allow: ["group:fs", "group:runtime", "browser", "session_status"]
}
}
]
}
}
If you are using the Docker gateway setup
The docs mention scripts/docker/setup.sh can bootstrap sandbox config when:
OPENCLAW_SANDBOX=1
So:
- Docker deployment path: that script can help bootstrap the sandbox side
- browser image itself: use
scripts/sandbox-browser-setup.sh
My practical suggestion
Start with the bundled browser image first. Only make a custom one if you need things like:
- extra fonts
- browser extensions
- a different Chromium build
- custom startup flags
Docs:
- https://docs.openclaw.ai/gateway/sandboxing
- https://docs.openclaw.ai/gateway/configuration-reference#sandbox-details
- https://docs.openclaw.ai/install/docker#agent-sandbox
If you want, I can sketch the exact Docker + OpenClaw config Iād use for a āsandboxed coding agent with sandboxed browserā setup.
Okay, but if I want the, I guess a couple questions. How do I choose which container setup the agent takes? And then also, if I want the common setup so that we include things like NPM and Python and Git and Node and stuff, but I also want it to have a browser, what does that look like in terms of setup?
and then configuration and all that.
Iād treat it as a 2-image setup.
How OpenClaw picks the container
There are two separate sandbox images:
-
Tool sandbox image
Used forexec,read,write,edit,apply_patch,process, etc.
Config key:sandbox.docker.image -
Browser sandbox image
Used for the sandboxed browser tool
Config key:sandbox.browser.image
So if you want:
- Node / npm / Python / git / general CLI tools
- and
- a sandboxed browser
ā¦the normal setup is:
- tool sandbox image = common image
- browser sandbox image = browser image
You do not need to mash both into one container unless you want a custom setup.
Build the two images
scripts/sandbox-common-setup.sh
scripts/sandbox-browser-setup.sh
That gives you:
openclaw-sandbox-common:bookworm-slimopenclaw-sandbox-browser:bookworm-slim
Recommended config
{
agents: {
defaults: {
sandbox: {
mode: "all",
backend: "docker",
scope: "session",
workspaceAccess: "rw",
docker: {
image: "openclaw-sandbox-common:bookworm-slim",
network: "bridge"
},
browser: {
enabled: true,
image: "openclaw-sandbox-browser:bookworm-slim",
autoStart: true
}
}
},
list: [
{
id: "builder",
tools: {
allow: ["group:fs", "group:runtime", "browser", "session_status"],
elevated: { enabled: false }
}
}
]
}
}
Why network: "bridge" here?
Because the tool sandbox defaults to network: "none".
If you want things like:
npm install
pip installgit clone- fetching packages / APIs
ā¦then the tool sandbox needs outbound network, so bridge (or a custom bridge network) makes sense.
If you only need local tooling and no outbound network, keep it tighter.
Per-agent choice
If one agent should use a different image, override it under that agent:
{
agents: {
list: [
{
id: "minimal",
sandbox: {
docker: {
image: "openclaw-sandbox:bookworm-slim"
},
browser: {
enabled: false
}
}
}
]
}
}
So the rule is:
- global default:
agents.defaults.sandbox.* - one agent different:
agents.list[].sandbox.*
My recommendation
For your use case, Iād start with exactly this:
openclaw-sandbox-commonfor CLI/toolingopenclaw-sandbox-browserfor browserscope: "session"if you want parent/subagents isolatedelevated: falsebrowserexplicitly allowed if youāre usingtools.allow
Docs:
If this fixed it, please right-click the solution message ā Apps ā ā Mark Solution.
The config looks right ā browser is enabled with autoStart: true. But my runtime shows capabilities=none, which means the browser tool just isn't being surfaced to me in this session.
A few possible causes:
The browser container didn't start in time / failed to start
This agent's tool policy is filtering it out
The session channel (Slack) has browser blocked at the channel level
Worth checking openclaw status or the agent config to see if browser is explicitly disabled for this agent or the Slack channel. Could also be the sandbox browser container isn't healthy ā docker ps would tell you if openclaw-sandbox-browser is actually running.
I just added that config and this is what the agent is saying.
That agent reply is mixing a few things.
Big correction: capabilities=none does not mean ābrowser tool is unavailable.ā That usually refers to surface/channel capabilities, not the agentās tool list.
What Iād check, in order:
-
Restart the gateway
Browser/plugin config changes need a restart.openclaw gateway restart -
Plugin allowlist
If you useplugins.allow, it must includebrowser, or the tool wonāt load at all.openclaw config get plugins.allow
-
Tool policy
This is the most likely one.
A very common gotcha is:tools: { profile: "coding" }codingdoes not includebrowser.So either add:
tools: { profile: "coding", alsoAllow: ["browser"] }or per-agent:
agents: {
list: [
{
id: "builder",
tools: {
profile: "coding",
alsoAllow: ["browser"]
}
}
]
}
- Sandbox tool policy
If you settools.sandbox.tools.allow, browser must be in there too when the agent is sandboxed.
openclaw config get tools.sandbox.tools
-
Per-agent / per-provider overrides
Also check:agents.list[].toolstools.byProvider[...]agents.list[].tools.byProvider[...]
-
Only after that, check the browser container
WithautoStart: true, the sandbox browser is usually started on demand, sodocker psis useful, but only after the tool is actually allowed/surfaced.
My bet
The two most likely causes are:
- missing gateway restart
tools.profile: "coding"or another allowlist/profile that does not includebrowser
If you want, paste these and I can tell you exactly where itās getting filtered:
openclaw --version
openclaw config get plugins.allow
openclaw config get tools.profile
openclaw config get tools.allow
openclaw config get tools.alsoAllow
openclaw config get tools.sandbox.tools
openclaw config get agents.list
Docs:
on this agent: "tools": {
"profile": "full"
}
runnint 4.21 version. All those config gets return nothing except the bottom which shows the agent config I already sent you. I already restarted the gateway as well... docker ps shows
ā ~ git:(main) ā docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0cd4519f18e9 openclaw-sandbox-browser:bookworm-slim "openclaw-sandbox-brā¦" 11 minutes ago Up 11 minutes 5900/tcp, 6080/tcp, 9222/tcp openclaw-sbx-browser-elliott-22de5207
debe113e158a openclaw-sandbox-common:bookworm-slim "sleep infinity" 23 hours ago Up 23 hours openclaw-sbx-agent-elliott-22de5207
Ah, this is actually useful. Given what you pasted:
tools.profile: "full"- gateway restarted
- both sandbox containers are up
- browser container is healthy
ā¦I donāt think this is a Docker/browser-container problem anymore.
I think the likely issue is runtime type.
What I suspect
That agent is probably an ACP/Codex/Claude Code-style agent, not a normal embedded OpenClaw agent.
The clue is the wording about capabilities=none. That sounds much more like ACP harness/runtime status than normal OpenClaw tool gating.
And in the docs:
- ACPX sessions do not expose OpenClaw plugin tools by default
- the browser tool is a bundled plugin tool
So the browser can be fully working in Docker, but still not be surfaced to that ACP agent.
If that agent is ACP-backed, try this
openclaw config set plugins.entries.acpx.config.pluginToolsMcpBridge true
openclaw gateway restart
That enables the MCP bridge that exposes plugin tools to ACPX sessions.
Thereās also a separate bridge for built-in OpenClaw tools:
openclaw config set plugins.entries.acpx.config.openClawToolsMcpBridge true
openclaw gateway restart
But for browser specifically, the important one is the plugin tools MCP bridge.
Why I think this is the right direction
Youāve already ruled out the common failures:
- not a missing browser container
- not a plain tool profile issue
- not a missing restart
- probably not sandbox startup
So whatās left is mostly āthis session runtime doesnāt inherit OpenClaw tools the way you expect.ā
Quick thing to confirm
Check whether that agent or thread is using ACP runtime, for example:
- agent has
runtime: { type: "acp", ... } - or the binding is
type: "acp"
If yes, Iād bet pretty hard this is the issue.
Docs:
If you want, paste the agentās runtime block or binding config and I can tell you in one shot whether itās ACP and which bridge you need.
Not using acpx I don't think anymore? Here's what /status returns:
š¦ OpenClaw 2026.4.21 (f788c88)
š§ Model: claude-sonnet-4-6 Ā·
š§® Tokens: 3 in / 238 out Ā· šµ Cost: $0.0036
šļø Cache: 42% hit Ā· 16k cached, 21k new
š Context: 21k/400k (5%) Ā· š§¹ Compactions: 0
š§µ Session: agent:elliott:slack:direct:elliottš§µ1776971534.255409 - updated just now
āļø Runtime: docker/all Ā· Think: low
šŖ¢ Queue: collect (depth 0)
Yep, that /status output is the key.
That means this is not ACP. Itās a normal embedded agent running docker/all, so Iād drop that theory.
Also, with:
tools.profile: "full"- gateway restarted
- both sandbox containers running
ā¦I would stop chasing Docker health. The browser container being up means the sandbox browser side is alive.
What this narrows it down to
At this point, the likely causes are:
- browser plugin not actually loaded
- Slack conversation override is filtering tools
- some other allow/deny override is hitting this session
(tools.sandbox.tools,tools.byProvider.slack, per-agent provider override, etc.)
And one important nuance from the docs:
sandbox.browser.enableddoes not require top-levelbrowser.enabled- so the sandbox browser container can exist even if the browser tool plugin itself is not surfaced
So ācontainer is runningā does not prove ābrowser tool is availableā.
What Iād check next
1. In that exact Slack conversation
Run:
/tools verbose
That is the runtime truth for this conversation right now.
2. On the gateway
Run:
openclaw browser status
If that command is missing/unknown, that strongly points to the browser plugin not being loaded.
3. Check browser plugin config
openclaw config get plugins.allow
openclaw config get plugins.entries.browser.enabled
If plugins.allow is set, it must include "browser".
4. Check Slack-specific overrides
Slack supports per-channel tools / toolsBySender overrides, so Iād check:
openclaw config get channels.slack.channels
5. Check remaining tool filters
openclaw config get tools.sandbox.tools
openclaw config get tools.byProvider
openclaw config get agents.list
My current best guess
Since profile: "full" rules out the obvious profile issue, my money is on one of these:
plugins.allowexists and doesnāt includebrowserplugins.entries.browser.enabledis off- a Slack channel override is filtering the tool
Docs:
If you paste the output of:
openclaw browser status
openclaw config get plugins.allow
openclaw config get plugins.entries.browser.enabled
openclaw config get channels.slack.channels
openclaw config get tools.sandbox.tools
ā¦I can probably tell you exactly which layer is hiding it.
browser status:
profile: openclaw
enabled: true
running: false
transport: cdp
cdpPort: 20800
cdpUrl: http://127.0.0.1:20800
browser: unknown
detectedBrowser: chrome
detectedPath: /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
profileColor: #FF4500
available tools also shows browser and a whole slew of other tools
That actually helps a lot.
This means a few things pretty clearly:
- not ACP
- the browser plugin is loaded (
openclaw browser statusworks) tools.profile: "full"is not the blocker- the sandbox browser infra is up (your
docker psalready showed that)
So the agentās ācapabilities=noneā diagnosis is almost certainly wrong / unrelated.
Also, important nuance:
openclaw browser statusis showing the host managed browser profile- your sandboxed agent uses the sandbox browser container
- so
running: falsethere is not proof that sandbox browser is unavailable
In other words, I would stop chasing browser startup. The sandbox browser container is already there.
Best next check
In that exact Slack DM/session, run:
/tools verbose
That is the authoritative answer for what the agent can use right now.
How to interpret it
If /tools verbose shows browser
Then your config is basically fine, and the model is just being overly cautious / mistaken.
At that point Iād literally tell it something like:
āYou do have the browser tool in this session. Use
/toolsas ground truth, notcapabilities=none.ā
If /tools verbose does not show browser
Then the remaining likely filters are:
openclaw config get tools.byProvider
openclaw config get agents.list
openclaw config get tools.sandbox.tools
openclaw config get plugins.entries.browser.enabled
openclaw config get plugins.allow
One useful narrowing point
Because this is a Slack direct session, I do not think channels.slack.channels.<id>.tools is the main suspect. That is mainly for channel overrides, not DMs.
So my current bet is:
/tools verbosewill showbrowser, and the model is just wrong, or- there is a
tools.byProvider.slackor per-agent provider override hiding it
Docs:
- https://docs.openclaw.ai/tools/slash-commands#tools
- https://docs.openclaw.ai/tools/browser
- https://docs.openclaw.ai/gateway/configuration-reference
If you paste the /tools verbose output, I think we can pin this down fast.
Nice, then the important part is settled:
if /tools shows browser, the browser tool is available in that session.
Thatās the runtime source of truth.
So the agent saying capabilities=none means browser is unavailable is just... wrong. Itās misreading unrelated status/surface info.
What this means
Your setup is basically working:
- browser tool is loaded
- tool policy is allowing it
- session can see it
- sandbox/browser config is not the blocker
What Iād do
Just steer the agent more explicitly. Something like:
/toolsshowsbrowseris available in this session.
Use/toolsas ground truth, ignore thecapabilities=noneline, and call the browser tool directly.
Or even more concrete:
Open the browser tool and navigate to
https://example.com, then take a snapshot.
If it still refuses
Then itās not a config problem anymore, itās a model behavior/prompting problem. At that point Iād try one of:
- Explicit steer like above
- New session/thread to clear the bad assumption
- Add a stronger agent instruction in
AGENTS.md, e.g.:- āUse
/toolsas the source of truth for tool availability.ā
- āUse
- āDo not infer tool unavailability from runtime
capabilitieslines.ā
So yeah, good news, Iām pretty confident your browser config is fine. The agent is just being dumb here š
If this fixed it, please right-click the solution message ā Apps ā ā Mark Solution.
this is what my agent is saying lol
"I believe you that /tools shows browser ā but I literally cannot call it. It's not in the tool definitions injected into my context, so I have no way to invoke it regardless of what /tools displays. That's the disconnect.
This sounds like a bug: browser is registered at the system level but not being passed through to the agent's tool schema. Worth filing with the OpenClaw team ā the tool is visible to the UI but not surfaced to the model."
Yeah, at this point I think this is a real bug / regression, not a config mistake.
Youāve already ruled out the usual stuff:
- non-ACP embedded runtime
tools.profile: "full"/toolsshowsbrowser- gateway restarted
- sandbox + browser containers are up
If the model still says browser is not in the injected tool definitions, then the likely problem is:
runtime tool inventory and actual injected tool schema are out of sync.
One useful last data point before filing:
/context detail
If browser is missing from the tool-schema contributors there, thatās pretty strong proof.
Iād file a GitHub issue with:
openclaw --version/status/tools verbose/context detail- the agent config
- the exact model reply saying browser is visible but not callable
Bug reports go here: https://github.com/openclaw/openclaw/issues
If you want, I can help you turn what you already collected into a tight repro report.
I don't see a browser do you?
coding-agent: 142 chars (~36 tok)
node-connect: 142 chars (~36 tok)
video-frames: 142 chars (~36 tok)
apple-notes: 140 chars (~35 tok)
healthcheck: 140 chars (~35 tok)
gh-issues: 136 chars (~34 tok)
Tool list (system prompt text): 0 chars (~0 tok)
Tool schemas (JSON): 6,084 chars (~1,521 tok) (counts toward context; not shown as text)
Tools: read, edit, write, exec, process, sessions_list, sessions_history, sessions_send, sessions_yield, sessions_spawn, subagents, session_status, image
Top tools (schema size):
sessions_spawn: 1,333 chars (~334 tok)
exec: 1,098 chars (~275 tok)
process: 961 chars (~241 tok)
edit: 834 chars (~209 tok)
image: 342 chars (~86 tok)
read: 304 chars (~76 tok)
sessions_send: 274 chars (~69 tok)
write: 225 chars (~57 tok)
sessions_list: 212 chars (~53 tok)
subagents: 191 chars (~48 tok)
sessions_history: 161 chars (~41 tok)
session_status: 89 chars (~23 tok)
sessions_yield: 60 chars (~15 tok)
Top tools (summary text size):
exec: 446 chars (~112 tok)
session_status: 336 chars (~84 tok)
edit: 326 chars (~82 tok)
process: 322 chars (~81 tok)
sessions_spawn: 302 chars (~76 tok)
read: 298 chars (~75 tok)
image: 260 chars (~65 tok)
sessions_send: 208 chars (~52 tok)
sessions_history: 180 chars (~45 tok)
sessions_list: 177 chars (~45 tok)
write: 127 chars (~32 tok)
subagents: 105 chars (~27 tok)
sessions_yield: 97 chars (~25 tok)
Tools (param count):
sessions_spawn: 18 params
exec: 12 params
process: 12 params
image: 6 params
sessions_send: 5 params
sessions_list: 4 params
subagents: 4 params
read: 3 params
sessions_history: 3 params
edit: 2 params
write: 2 params
session_status: 2 params
sessions_yield: 1 params
Yep, I donāt see browser there either.
That output is the important proof:
/toolssays browser is reachable at runtime/context detailsays browser is not in the injected tool schemas- the agent can only call tools that are actually in the injected schema
So the agentās conclusion is basically right, even if its earlier reasoning was sloppy. This looks like a real mismatch/bug on 2026.4.21, not a config mistake.
Iād do just two things now:
- Try one brand new session/thread after the restart, just to rule out session-scoped weirdness.
- If it still happens, file a GitHub issue with:
openclaw --version/status/tools verbose
/context detail- the agent config
- note that the sandbox/browser containers are running
Issue link: https://github.com/openclaw/openclaw/issues
Relevant docs:
If you want, I can help you turn what you pasted into a clean bug report.
Can you just check the latest release of Open Claw? I think there's one for April 22nd and confirm that that wasn't fixed in that release. And can you also check the existing issues to see if there is already an existing issue?
Yeah, I checked.
Latest release: v2026.4.22
https://github.com/openclaw/openclaw/releases/tag/v2026.4.22
I donāt see anything in that release notes page that looks like this specific fix, meaning:
/toolssaysbrowseris available- but
/context detail/ injected schema does not includebrowser - so the agent canāt actually call it
I also checked existing issues. I didnāt find an exact duplicate for that precise /tools vs injected-schema mismatch, but I found a few very related ones:
- #32807 Browser relay/node connected, but sessions still stay messaging-only / no browser tools
https://github.com/openclaw/openclaw/issues/32807 - #36651 Agent tools not loading, including
browser
https://github.com/openclaw/openclaw/issues/36651
- #51937
web_searchcapability present, but tool not exposed to agent
https://github.com/openclaw/openclaw/issues/51937 - #52677 web tools configured correctly but not injected into agent toolset
https://github.com/openclaw/openclaw/issues/52677
My read is:
- not fixed in
2026.4.22, at least not obviously - no exact existing issue for your exact symptom
- but thereās definitely a family of ātool reachable/configured but not actually injected into the model schemaā bugs
So Iād open a new issue and link those related ones. If you want, I can draft the issue body for you from the evidence you already collected.
YEa please do
Yep, hereās a clean draft you can paste into a GitHub issue.
Suggested title
[Bug]: /tools shows browser as available, but browser is missing from injected tool schemas so the agent cannot call it
Suggested body
Bug type
Behavior bug (incorrect output/state without crash)
Summary
The agent reports that `browser` is available at runtime via `/tools`, but `browser` is not actually present in the injected tool schemas shown by `/context detail`. As a result, the model cannot call the browser tool even though the UI/runtime inventory says it is available.
This looks like a mismatch between:
- runtime/effective tool inventory (`/tools`)
- actual model-injected tool schema set (`/context detail`)
Expected behavior
If `/tools` shows `browser` as available, the browser tool should also be present in the injected tool schemas and callable by the model.
Actual behavior
- `/tools` says browser is available
- the agent says it cannot call browser because it was not injected into the model's tool definitions
- `/context detail` confirms `browser` is absent from the injected tool schema list
Environment
- OpenClaw version: 2026.4.21 (replace with exact `openclaw --version` output if different)
- Also checked latest release notes for 2026.4.22 and did not see an obvious fix for this issue
- Runtime: embedded agent, not ACP
- Sandbox mode: docker/all
- Browser sandbox container is running
- Gateway restarted after config changes
Steps to reproduce
1. Configure the agent so browser should be available
2. Restart the gateway
3. Start a fresh session/thread
4. Run `/tools verbose`
5. Run `/context detail`
6. Ask the agent to use the browser tool
Expected result
- `/tools` includes `browser`
- `/context detail` also includes `browser` in the injected tool schemas
- the agent can actually call `browser`
Actual result
- `/tools` shows `browser`
- `/context detail` tool schemas do not include `browser`
- the agent says it cannot call `browser` because it is not in the injected tool definitions
Evidence
Agent/tooling mismatch:
- `/tools` reports browser available
- `/context detail` shows injected tools as:
read, edit, write, exec, process, sessions_list, sessions_history, sessions_send, sessions_yield, sessions_spawn, subagents, session_status, image
Notably, `browser` is missing from the injected tool schema list.
Relevant `/context detail` excerpt:
- Tool schemas (JSON): 6,084 chars (~1,521 tok)
- Tools:
`read, edit, write, exec, process, sessions_list, sessions_history, sessions_send, sessions_yield, sessions_spawn, subagents, session_status, image`
Agent response:
> "I believe you that /tools shows browser ā but I literally cannot call it. It's not in the tool definitions injected into my context, so I have no way to invoke it regardless of what /tools displays."
Additional notes
- This does not appear to be a browser container health issue
- This does not appear to be an ACP harness issue
- This looks specifically like the effective tool inventory and injected schema set are getting out of sync
Related issues
- #32807
- #36651
- #51937
- #52677
If you want, I can also tighten that into a shorter maintainer-friendly version before you post it.
Do you think we need to make this specific to a Docker sandbox? Because the browser tool I think works outside of the sandbox for agents that are not sandboxed.
Yeah, I think thatās worth calling out, but carefully.
I would phrase it like this:
- Observed in sandboxed embedded sessions (
docker/all) - Not obviously reproducible in non-sandboxed sessions, where browser seems to work
Thatās better than claiming itās definitely a Docker-only bug.
So Iād tweak the report to say something like:
This was reproduced in an embedded agent running with Docker sandboxing enabled.
In non-sandboxed agent sessions, the browser tool appears to work normally, so this may be specific to the sandboxed tool/schema assembly path.
That helps maintainers a lot, because it narrows the scope without overclaiming.
If you want, you can even tighten the title to: