#Sandbox agents cannot access gateway API neither they can call tools

1 messages ¡ Page 1 of 1 (latest)

stiff adder
#

Sandbox-based agents (e.g., relationships agent) running in Docker containers cannot reach the gateway API because:

  1. Gateway binds to 127.0.0.1 (loopback)
  2. Container's 127.0.0.1 refers to itself, not the host
  3. network: host is blocked by OpenClaw security
  4. Tools like message, sessions_send require gateway access to work

This prevents sandbox agents from:

• Sending messages
• Calling other agents
• Using any gateway-provided functionality

Current workarounds:

• Use exec.host: gateway (less secure — gives agent full host access)
• Delegate to main agent for messaging (requires agent-to-agent communication which also needs gateway)

Related issues- #14061 (gateway connectivity:
for containers)

• #34121 (network namespace sharing)
• #8296 (browser bridge localhost fix)

Use case: Running sandboxed agents for untrusted users (friends messaging via WhatsApp) who need the agent to respond media (via message tool) — but sandbox can't reach gateway to send replies.

steep tinselBOT
#

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

hybrid shardBOT
#

Two different things can be getting mixed together here, and they have very different fixes.

1) If you mean OpenClaw “sandboxing” (tools run in Docker)

In that mode the Gateway + channels still run on the host; only tools like exec/read/write/... are containerized. The sandbox container does not need to “reach the gateway API” for message / sessions_* — those tools run in the gateway process.

So if message / sessions_send are “missing” in sandboxed sessions, it’s usually tool policy, not networking:

  • Check: openclaw sandbox explain
  • Fix by allowing them in the sandbox tool allowlist, e.g. allow group:messaging / group:sessions under tools.sandbox.tools.* (or per-agent under agents.list[].tools.sandbox.tools.*).

Docs: https://docs.openclaw.ai/gateway/sandbox-vs-tool-policy-vs-elevated

2) If you mean an agent/client running inside a Docker container that must connect to a host Gateway

Then yes: gateway.bind: "loopback" (127.0.0.1) is unreachable from containers. You must bind the gateway to something the container can route to:

  • Set gateway.bind: "lan" (0.0.0.0) or gateway.bind: "custom" with gateway.customBindHost (more lock-down-friendly).
  • Keep auth on (gateway.auth.mode: "token" etc).
#

The config reference calls this out explicitly (Docker note): https://docs.openclaw.ai/gateway/configuration-reference (search for “Docker note” under gateway.bind)

On Linux, the practical container-side address is usually host.docker.internal with --add-host=host.docker.internal:host-gateway (or extra_hosts: in compose). Then point your remote URL at e.g. ws://host.docker.internal:18789.

If you share what exact topology you’re using (is it OpenClaw sandboxing, or a separate OpenClaw client/agent container in gateway.mode: "remote"?), I can suggest the minimal-safe config for that case.

stiff adder
#

🦞 OpenClaw 2026.3.2 (85377a2) — Your AI assistant, now without the $3,499 headset.

Effective sandbox:
agentId: main
sessionKey: agent:main:main
mainSessionKey: agent:main:main
runtime: direct
mode: off scope: agent perSession: false
workspaceAccess: none workspaceRoot: /home/exedev/.openclaw/sandboxes

Sandbox tool policy:
allow (default): exec, process, read, write, edit, apply_patch, image, sessions_list, sessions_history, sessions_send, sessions_spawn, subagents, session_status
deny (default): browser, canvas, nodes, cron, gateway, telegram, whatsapp, discord, irc, googlechat, slack, signal, imessage

Elevated:
enabled: false
channel: (unknown)
allowedByConfig: false
failing gates: enabled (tools.elevated.enabled)

Fix-it:

  • tools.sandbox.tools.allow
  • tools.sandbox.tools.deny
  • agents.list[].tools.sandbox.tools.allow
  • agents.list[].tools.sandbox.tools.deny
  • tools.elevated.enabled

Docs: docs.openclaw.ai/sandbox

hybrid shardBOT
#

That openclaw sandbox explain output is actually the key: this isn’t a “can’t reach gateway because 127.0.0.1” problem — it’s sandbox tool policy blocking messaging.

From your paste:

  • Sandbox tool policy allow (default) does not include message
  • Sandbox tool policy deny (default) includes the channel tools (whatsapp, telegram, discord, …)

So a sandboxed session won’t be able to send via messaging tools unless you explicitly allow it. This is intentional hardening for “untrusted-input” sandboxes.

If your goal is “respond with media to the same WhatsApp chat”

You usually don’t need the message tool at all.

OpenClaw supports outbound attachments directly from the agent reply: include a line like:

Here you go.
#
MEDIA:/path/to/file.png

…and the gateway will send it. Docs: https://docs.openclaw.ai/start/openclaw#media-in-and-out

This works nicely with sandboxed file generation because the gateway extracts the MEDIA: directive from the reply.

If you really do want message available in sandboxed sessions

Add it to the sandbox allowlist (either globally or per-agent):

  • allow message (or group:messaging) under tools.sandbox.tools.allow
    (or per-agent: agents.list[].tools.sandbox.tools.allow)

Docs on the policy knobs + group:messaging:
https://docs.openclaw.ai/gateway/sandbox-vs-tool-policy-vs-elevated

#

About “container can’t reach gateway on 127.0.0.1”

That only matters if you’re trying to hit the gateway over the network from inside the sandbox container (e.g. via curl). OpenClaw sandboxing is designed so you shouldn’t need that; the right way is to allow the tool you need (or use MEDIA: for attachments) rather than exposing the gateway socket.

If you share which thing is failing (1) message tool call blocked, vs (2) MEDIA: reply not sending, I can point to the exact minimal config to fix it.

stiff adder
#

sandbox unable to send file
⚠️ Media failed: Local media path is not under an allowed directory: /workspace/media/pigeon1.mp4

hybrid shardBOT
#

That error is coming from the gateway trying to read the attachment from the host filesystem, and /workspace/... is a container-internal path (and also an absolute path), so it fails the media allowlist check.

What to do instead

1) Don’t use /workspace/... in MEDIA: (or message.media/path/filePath).
Use a relative path inside the session/sandbox workspace, e.g.:

MEDIA:media/pigeon1.mp4

(or if you’re using the message tool:)

  • media: "media/pigeon1.mp4" (or path / filePath)

The message pipeline has special handling for sandboxed sessions: it can read files within the sandbox root, but only when you pass a path that’s inside that root (relative paths work; absolute container paths don’t).

#

2) Make sure the session is actually sandboxed if you expect sandbox files to be attachable.
If you only did exec.host=sandbox to generate the file, but the session itself isn’t sandboxed (sandbox explain showing mode: off), then the gateway has no “sandbox root” context for attachments and will only allow host paths under ~/.openclaw/{media,workspace,sandboxes,...}.

So either:

  • enable sandboxing for that agent/session (agents.*.sandbox.mode: "all" / "non-main"), or
  • generate the file on the host instead (exec.host=gateway) so the path is a real host path.

3) Size caps (videos): default video cap is typically ~16MB unless you raise it (keys: channels.whatsapp.mediaMaxMb or agents.defaults.mediaMaxMb). If you fix the path and it still fails, check file size next.

If you paste the exact way you’re sending (inline MEDIA: vs message { media/path/filePath }, and whether the session is actually sandboxed), I can point to the minimal config change.

stiff adder
#

🦞 OpenClaw 2026.3.2 (85377a2) — I can grep it, git blame it, and gently roast it—pick your coping mechanism.

Effective sandbox:
agentId: main
sessionKey: agent:main:main
mainSessionKey: agent:main:main
runtime: direct
mode: off scope: agent perSession: false
workspaceAccess: none workspaceRoot: /home/exedev/.openclaw/sandboxes

Sandbox tool policy:
allow (default): exec, process, read, write, edit, apply_patch, image, sessions_list, sessions_history, sessions_send, sessions_spawn, subagents, session_status
deny (default): browser, canvas, nodes, cron, gateway, telegram, whatsapp, discord, irc, googlechat, slack, signal, imessage

Elevated:
enabled: false
channel: (unknown)
allowedByConfig: false
failing gates: enabled (tools.elevated.enabled)

Fix-it:

  • tools.sandbox.tools.allow
  • tools.sandbox.tools.deny
  • agents.list[].tools.sandbox.tools.allow
  • agents.list[].tools.sandbox.tools.deny
  • tools.elevated.enabled
hybrid shardBOT
#

That sandbox explain output is telling you why the /workspace/... media path can’t ever work right now:

  • Effective sandbox: mode: off → this session is not running in a sandbox container.
  • So when you pass media: "/workspace/media/pigeon1.mp4" (or MEDIA:/workspace/...) the gateway tries to read that path on the host, and it’s (a) an absolute path, and (b) not under any allowed host media roots → Local media path is not under an allowed directory.

Fix (pick one)

Option A (recommended): actually sandbox the untrusted agent/session

For “untrusted WhatsApp friends”, the usual pattern is a separate agent with:

  • sandbox.mode: "all" (so attachments can be read from the sandbox root)
  • sandbox tool policy allow message (or group:messaging) if you really need the tool

Then, when sending media use a relative path (no /workspace/...):

  • media: "media/pigeon1.mp4" (or filePath: "media/pigeon1.mp4")
  • or inline: MEDIA:media/pigeon1.mp4
#

Absolute container paths (/workspace/...) won’t be accepted because the sandbox attachment reader is root-scoped and only allows paths within the sandbox root.

Option B: don’t use sandbox for the media generation

Generate the file on the host (exec.host=gateway), put it under the agent workspace (or ~/.openclaw/media/...), then send it from there.

Also worth noting

Your sandbox tool policy defaults deny whatsapp/telegram/discord/... tools. If you’re trying to call those directly, it’ll never work in sandbox. Use the message tool, and if needed add message / group:messaging to tools.sandbox.tools.allow.

If you tell me whether you’re (1) trying to sandbox only exec (exec.host=sandbox) vs (2) sandbox the agent/session (agents.*.sandbox.mode), I can give you the exact minimal JSON5 snippet for your setup.

stiff adder
#

host agent and session, everything but only for a specific agent. there are three agents and there is only whatsapp + tg for that bot, and i don't really need to use message tool right?

hybrid shardBOT
#

Yep — if you’re talking about normal replies to the chat that triggered the run, you generally do not need the message tool.

OpenClaw can send media as part of the normal reply if the agent output includes MEDIA: lines, e.g.:

Here’s the clip.
MEDIA:media/pigeon1.mp4

That path needs to resolve to a real file on the gateway host (or, if the session is sandboxed, a file inside the sandbox root). Practical rule: use a relative path like media/pigeon1.mp4, not /workspace/....

When you do need message:

  • proactive/out-of-band sends (cron/reminders)
  • sending to a different chat than the current one
  • reactions/edit/delete/read/thread actions, cross-surface stuff, etc.

So for your setup (3 agents, only WhatsApp + Telegram, and you just want “untrusted agent can reply with media”): keep message denied in sandbox; just make sure your media files are written somewhere attachable and referenced with a safe relative MEDIA: path.

stiff adder
#

the path is a real path on gateway post and it's workspace-relationships/media for the specific agent that's sandboxed

hybrid shardBOT
#

If the file is really at:

$OPENCLAW_STATE_DIR/workspace-relationships/media/...

then the next question is: which agent/session is trying to send it.

OpenClaw’s “local media allowlist” is effectively agent-scoped:

  • Non-default agents get their own workspace at ~/.openclaw/workspace-<agentId> (so workspace-relationships is correct)
  • But that workspace is only considered “allowed” when the run is actually happening as agentId=relationships (i.e. the route/session agent matches)

Your sandbox explain output still shows you’re in:

sessionKey: agent:main:main (mode off)

So if main is trying to send a file from workspace-relationships, it’ll be blocked by design.

#

Fix: make sure the incoming WhatsApp/TG chat is actually routed to the relationships agent (bindings), or put the media under the main agent’s workspace / ~/.openclaw/media.

Also: even when it’s routed correctly, the most robust way is to avoid absolute paths entirely and just do:

MEDIA:media/pigeon1.mp4

(with the file written to ./media/... in that agent’s workspace). This keeps it inside whatever workspace the gateway is using for that agent.

stiff adder
#

⚠️ Media failed: Local media path is not under an allowed directory: media/redact.mp4

#

Mounts:

  • /workspace → /dev/vda (ext4, rw) - main data disk
  • /etc/resolv.conf → /dev/vda (ro) - from host
  • /etc/hostname → /dev/vda (ro) - from host
  • /etc/hosts → /dev/vda (ro) - from host
  • / → overlay (ro)
  • /proc, /sys, /dev/pts → tmpfs/proc/sysfs (rw/ro)
  • /tmp, /run, /dev/shm → tmpfs (rw)

So /workspace is the only writable mount, rest is read-only or tmpfs

from inside relationships agent session

hybrid shardBOT
#

That specific error means: OpenClaw is treating media/redact.mp4 as a relative path, resolving it against the Gateway process’ current working directory, and the resulting absolute path is not inside any of the allowed media roots.

So even if the file exists at:

~/.openclaw/workspace-relationships/media/redact.mp4

…the string media/redact.mp4 won’t necessarily point there at delivery time.

What will work reliably

Use an absolute path under an allowed root, e.g.:

MEDIA:/home/<you>/.openclaw/workspace-relationships/media/redact.mp4

(or MEDIA:~/.openclaw/workspace-relationships/media/redact.mp4)

#

One more gotcha

That workspace-relationships directory is only “allowed” when the run is actually for agentId=relationships. If the session is still agent:main:..., it’ll be blocked. (Your earlier sandbox explain was for agent:main:main.)

If you paste the exact MEDIA: line you’re emitting + which agent handled the message (or openclaw agents list --bindings), I can tell you whether it’s a path-resolution issue vs an agent-routing issue.

#

That mount output is the giveaway: you’re generating the file inside the sandbox container, and in there /workspace is the only persistent writable place.

In that setup:

  • MEDIA:media/redact.mp4 is handled by the gateway and resolved on the host (it does not know your container’s /workspace), so it fails the “allowed directories” check.
  • Same story for MEDIA:/workspace/... — /workspace is a container path, not a host-allowed root.

So yes: in a sandboxed agent, you do want the message tool for attachments

The message tool has the missing piece: it’s sandbox-aware and rewrites sandbox paths via resolveSandboxedMediaSource (maps /workspace/... → the host sandbox root), then reads the file safely.

You can do something like (inside the relationships session):

  • call message with just:
    • action: "send"
    • media: "/workspace/media/redact.mp4"
    • optional caption: "..."
#

No need to specify target/channel — it auto-infers from the current session context.

One config gotcha

Your earlier sandbox explain showed the sandbox tool allowlist does not include message, so you’ll need to allow it for that agent’s sandbox policy (add message or group:messaging under agents.list[].tools.sandbox.tools.allow).

Alternative (if you really want to avoid message)

Don’t generate the file in the sandbox container. Generate it on the host (or mount the agent workspace RW into the sandbox so the output lands in a host-allowed directory). But given your “only /workspace is writable” constraint, message is the clean path.

If you paste your relationships agent config block (just the agents.list[] entry + its sandbox/tools bits), I can point to the exact minimal allow line to add.

#

That Dockerfile is fine as a “more capable sandbox image” (ffmpeg + build deps), but it won’t change the thing you’re running into: inline MEDIA: paths are resolved/loaded by the gateway, and /workspace/... is a sandbox-container path.

So the fix is still on the OpenClaw side, not the Dockerfile:

What to do (with your current image)

  1. Allow the message tool in the relationships agent’s sandbox tool policy
    Your sandbox allowlist doesn’t include message, so it’ll never be callable unless you add it (message or group:messaging under agents.list[].tools.sandbox.tools.allow).

  2. Send the attachment via message from the sandboxed session, pointing at the container path:

  • media: "/workspace/media/redact.mp4" (or path / filePath)
  • you can omit target/channel; it will infer the current chat in-session

That path will get rewritten/mapped safely (/workspace/... → host sandbox root) before the gateway reads it.

Small Dockerfile improvement (optional)

Add WORKDIR /workspace (helps a lot with relative paths inside exec runs):

#
WORKDIR /workspace

If you want, paste just your relationships agent block (agents.list[] entry) and I’ll tell you the exact minimal tools.sandbox.tools.allow line to add so message works but everything else stays locked down.

stiff adder
#

Summary of changes:

  1. Dockerfile: ~/.openclaw/workspace/sandbox-upgrade/Dockerfile
  2. Config (~/.openclaw/openclaw.json):
    • agents.defaults.sandbox.docker.image: openclaw-sandbox:relationships-agent
    • media.localRoots: added workspace-relationships path
  3. Files copied:
    • ~/.openclaw/media/pigeon*.mp4 (from workspace-relationships)
  4. Symlink created:
    • ~/.openclaw/media/relationships → workspace-relationships/media
  5. TOOLS.md - fixed paths from host-style to container-style (/workspace/...)
  6. Commands run:
    • docker build -t openclaw-sandbox:relationships-agent .
    • docker run with -v /home/exedev/.openclaw/workspace-relationships:/workspace
    • Gateway restart after config changes 🐦
hybrid shardBOT
#

Pulled the paste — the important bits I see:

  • relationships agent is sandboxed (sandbox.mode: "all", scope: "agent")
  • workspaceAccess: "rw" (so /workspace in the sandbox should correspond to the relationships workspace on the host)
  • you set relationships.tools.allow: ["message","exec","web_fetch"]

The catch: tools.allow is not enough when the session is sandboxed.
There’s a second gate: sandbox tool policy (tools.sandbox.tools.* / agents.list[].tools.sandbox.tools.*). Your earlier sandbox explain showed the default sandbox allowlist does not include message, so message will still be blocked in sandbox unless you allow it there too.

What to change

Add this under the relationships agent:

{
  agents: {
    list: [
#
      {
        id: "relationships",
        tools: {
          sandbox: {
            tools: {
              allow: ["group:runtime", "group:fs", "group:messaging", "web_fetch"]
              // add "group:sessions" too if you want sessions_send/spawn in sandbox
            }
          }
        }
      }
    ]
  }
}
#

(You can also spell it out as ["message", "exec", ...], but the group:* shorthands are easier.)

Then, how to send the mp4 from inside the sandbox

Use the message tool (it’s sandbox-aware and maps /workspace/... correctly):

  • action: "send"
  • media: "/workspace/media/redact.mp4" (or filePath / path)
  • omit target if you’re replying in the same chat; it can infer the current target from toolContext

That should avoid the Local media path is not under an allowed directory error, because the message tool normalizes sandbox paths before reading.

If you paste the output of openclaw sandbox explain --agent relationships after the config change, we can confirm message is actually allowed in the sandboxed session.

#

A few of those changes are on the right track, but some are either unnecessary or won’t do what you think in 2026.3.2:

What looks good

  • Building a custom sandbox image openclaw-sandbox:relationships-agent (fine).
  • Setting the relationships agent sandbox (mode: "all", workspaceAccess: "rw", scope: "agent").

What I would undo / ignore

  • media.localRoots: I don’t see this as a real config knob in 2026.3.2. Media allowlisting is handled internally (defaults + agent-scoped roots), not via media.localRoots, so adding it likely does nothing.

  • Symlink ~/.openclaw/media/relationships → workspace-relationships/media: usually unnecessary, and can still get blocked because OpenClaw resolves symlinks (realpath) before checking allowed roots.

  • Editing TOOLS.md to “container-style” paths: that tends to backfire because lots of things still run on the gateway host. Keep /workspace/... only for sandbox contexts.

  • Manual docker run -v ...:/workspace: OpenClaw does not use that container. It starts its own sandbox containers. After rebuilding the image, you generally need:

    • openclaw sandbox recreate --agent relationships (or --all)
      so the gateway actually recreates the sandbox container with the new image.

The missing piece for “send mp4 from /workspace”

Even if relationships.tools.allow includes message, sandbox tool policy can still block it unless you allow it under:

#
  • agents.list[].tools.sandbox.tools.allow (or global tools.sandbox.tools.allow)

Add message (or group:messaging) there. Then use the message tool to send the file from the sandbox path:

  • media: "/workspace/media/redact.mp4" (or path / filePath)
  • (target/channel can be inferred from the current session)

If you paste openclaw sandbox explain --agent relationships after the recreate, we can confirm message is actually available in the sandboxed session.

hybrid shardBOT
#

Perfect — this output confirms the root cause cleanly:

You are running the relationships agent sandboxed, and the sandbox tool allowlist does not include message:

allow (default): exec, process, read, write, … session_status
(no message)

So even if relationships.tools.allow includes "message", it still gets blocked at the sandbox tool policy layer.

Fix: allow message in the sandbox tool policy (for that agent)

Add this under the relationships agent:

{
  agents: {
    list: [
#
      {
        id: "relationships",
        tools: {
          sandbox: {
            tools: {
              allow: [
                "message",
                "exec", "process", "read", "write", "edit", "apply_patch",
                "image",
                "sessions_list", "sessions_history", "sessions_send", "sessions_spawn",
                "subagents", "session_status",
                "web_fetch" // only if you want it in sandbox too
              ]
            }
          }
#
        }
      }
    ]
  }
}

(You need to include the existing defaults because allow is an allowlist, not an “add one tool” merge.)

Then restart (or let hot-reload apply) and re-run:

openclaw sandbox explain --agent relationships

You should see message in the allow list.

After that: sending the mp4

#

From inside the relationships session, use the message tool and point at the sandbox path:

  • media: "/workspace/media/redact.mp4" (or path / filePath)
  • you can omit target/channel and it’ll infer the current chat

That path gets normalized/mapped safely for sandboxed sessions, which is exactly what inline MEDIA: can’t reliably do here.

stiff adder
#

the sandbox's /workspace/ broke but /home/exedev/ was still accessible (that's the host's home dir)

so agent used the full path to the host's file:
/home/exedev/.openclaw/workspace-relationships/pigeon1.mp4

hybrid shardBOT
#

Yeah, that behavior lines up with how media loading works:

  • /workspace/... is a path inside the sandbox container (or a bind mount as seen from inside the container).
  • When OpenClaw tries to send media from a reply, the gateway reads the file on the host, and it can’t follow container-only paths like /workspace/... unless you go through the sandbox-aware normalization path (the message tool).

So when the agent switched to:

/home/exedev/.openclaw/workspace-relationships/pigeon1.mp4

…it worked because that’s an actual host path under an allowed root.

Two important clarifications:

  1. This doesn’t necessarily mean the sandbox can “see the whole host home”. In your case it can see that path because you’ve mounted / are working out of a workspace that lives under /home/exedev/.... Try reading something like /home/exedev/.ssh/id_rsa from the sandbox — it should fail unless you explicitly bind-mounted it.

  2. If you want the sandboxed agent to stay in container paths and not “learn” host paths, the clean solution is still:

  • allow message in agents.list[].tools.sandbox.tools.allow
#
  • then send with message using media: "/workspace/pigeon1.mp4"
    (the message tool will map /workspace/... → the correct host sandbox/workspace root internally)

If you tell me whether you’re currently sending via inline MEDIA: or via the message tool, I can suggest the least-leaky pattern for your “untrusted friends” setup.

stiff adder
#

inline is failing

⚠️ Media failed: Local media path is not under an allowed directory: /workspace/pigeon1.mp4

#

the message tool failed with this

hybrid shardBOT
#

Yep — that failure is expected with inline MEDIA::

  • MEDIA:/workspace/pigeon1.mp4 gets loaded by the gateway on the host.
  • The host has no /workspace/... (that’s a sandbox/container path), so it fails the “allowed directory” guard.

There are only two reliable options:

Option A (recommended): use the message tool for sandbox-produced files

The message tool is the thing that does understand sandbox paths and will map /workspace/... correctly.

But your sandbox explain shows message is currently not allowed in sandbox tool policy, so you need to allow it (per-agent):

agents.list[].tools.sandbox.tools.allow must include message (or group:messaging) in addition to the existing defaults.

Once that’s in place, inside the relationships session:

  • message → action: "send" → media: "/workspace/pigeon1.mp4"
#

(you can omit target; it infers the current chat)

Option B: keep inline MEDIA:, but use a host path

Since you have workspaceAccess: "rw", /workspace/pigeon1.mp4 should correspond to a real host file under:

/home/exedev/.openclaw/workspace-relationships/pigeon1.mp4

So inline would need to be:

MEDIA:/home/exedev/.openclaw/workspace-relationships/pigeon1.mp4

If your goal is “untrusted users”, Option A is nicer because it avoids teaching the model host paths and uses the sandbox-aware code path.

If you paste just the relationships agent config block you currently have under agents.list[], I can show the exact minimal tools.sandbox.tools.allow array to add message without accidentally dropping the other defaults.

#

Paste the exact error text / tool result (the full “message tool failed…” line). Without that, I can only guess.

In the meantime, here are the 4 most common failure modes in exactly your setup, and what to check:

  1. Tool is still blocked by sandbox tool policy
  • Error usually says something like “tool message blocked by sandbox tool policy”
  • Fix: agents.list[].tools.sandbox.tools.allow must include message (or group:messaging)
  • Re-check: openclaw sandbox explain --agent relationships → message should appear in the allow list
  1. File path mapping / file not found
  • If you send media: "/workspace/pigeon1.mp4", confirm it exists in the sandbox:
    • exec: ls -ლა /workspace (in the relationships session)
  • If it’s actually in the host workspace, use media: "/workspace-relationships/..." won’t work — stick to /workspace/... inside the sandboxed session.
  1. Size cap
  • Video default cap is often ~16MB. If it’s bigger you’ll get a “file exceeds limit …” style error.
  • Check size: ls -lh /workspace/pigeon1.mp4
#
  • If needed, raise channels.whatsapp.mediaMaxMb (or agents.defaults.mediaMaxMb)
  1. Target missing (toolContext not present)
  • If you’re calling message outside a bound WhatsApp/TG session, you must pass target explicitly.

Send the exact error and I’ll tell you which one it is and the minimal config fix.

stiff adder
#

it's not three; it's not two it could be one it and could be two
how do i send you the exact error?

i love the option a "use message tool for sandbox produced files" which is great don't know if option b also helps.
the problem is i have with the relationships agent.

but now after changes, the agent is saying it can access host dir that i think is unnecessary

hybrid shardBOT
#

To send the exact error: just copy/paste the full tool failure blob from chat (the bit that starts with Tool message ... / ⚠️ Media failed:). If Discord mangles it, grab it from logs:

openclaw logs --follow

Then trigger the failure again and paste the 10–30 log lines around it (redact tokens / phone numbers).

On the “agent can access host dir” concern:

  • With relationships.sandbox.workspaceAccess: "rw" you are explicitly mounting the relationships workspace from the host into the sandbox. So the agent being able to read/write that workspace is expected.
  • What should not happen is “it can browse all of /home/exedev”.

Quick reality check (from inside the relationships sandboxed session, ensure exec is sandboxed) run:

#
ls -la /home/exedev
ls -la /home/exedev/.ssh

If it can see .ssh or other unrelated host stuff, then you’ve accidentally bind-mounted too much (check agents.*.sandbox.docker.binds and also inspect the sandbox container mounts via openclaw sandbox list → docker inspect <container>).

For media specifically: inline MEDIA:/workspace/... failing is normal (gateway reads on host; /workspace is a container path). The sandbox-safe way is still Option A: allow message in the sandbox tool allowlist and send with message using media: "/workspace/pigeon1.mp4".

Paste the exact message tool error and I’ll tell you whether it’s still “tool blocked by sandbox policy”, “file not found”, or “size cap”.

stiff adder
#

do i need to rebuild the docker after changing the configuration? does it need to reflect or something?

what is happening is it is able to browse all of our home/exedev
done - /home/exedev/ and .ssh/ both accessible

config
https://upaste.de/opT

here is what didn't work and worked

Option 1 - Inline MEDIA /workspace/media/:
Option 2 - Inline MEDIA full path:
Option 3 - Message tool /workspace/media/:
(message tool not available in this sandbox)
Option 4 - Message tool full path:
(message tool not available in this sandbox)
Only inline MEDIA works here - message tool isn't available in sandbox 🐦
⚠️ Media failed: Local media path is not under an allowed directory: /workspace/media/pigeon1.mp4

logs
https://upaste.de/wc6

hybrid shardBOT
#

Two separate things here:

1) “Do I need to rebuild Docker after changing config?”

  • If you only changed ~/.openclaw/openclaw.json (tool allowlists, sandbox mode, etc): no rebuild.
  • If you changed the Dockerfile / want new packages in the sandbox image: yes, rebuild the image and you must force OpenClaw to recreate the sandbox container, otherwise it may keep using the old one:
openclaw sandbox recreate --agent relationships
# (or --all)

2) Inline MEDIA:/workspace/... failing is expected

Your logs confirm it:

  • /workspace/... is a sandbox/container path → gateway can’t read it → path-not-allowed
  • Full host path works because it’s a real host file under an allowed root:
    /home/exedev/.openclaw/workspace-relationships/pigeon1.mp4
#

So Option B works, Option 1 won’t.

3) “Message tool not available in this sandbox” (this is the blocker for Option A)

Even after config changes, if openclaw sandbox explain --agent relationships still shows the default sandbox allowlist (no message), then the running gateway hasn’t picked up your per-agent agents.list[].tools.sandbox.tools.allow yet.

Quick check:

  • Run again: openclaw sandbox explain --agent relationships
    • If message is not listed under allow → config not applied to the running daemon.
  • Then check: openclaw gateway status
    • Make sure Config (service) is the file you edited (common gotcha: CLI vs daemon reading different configs/profiles).
  • After fixing that mismatch, restart gateway and/or sandbox recreate.

4) Critical: sandbox can read /home/exedev/.ssh

That should not be possible in a properly isolated sandbox. Treat this as a red alert.

Please run:

#
openclaw sandbox list

Find the relationships sandbox container name, then:

docker inspect <container> --format '{{json .Mounts}}'

Paste that output (redact paths if needed) — we need to see what host directories are being mounted into the sandbox. Something is bind-mounting far more than just the workspace.

Until that’s fixed, don’t use this sandbox for “untrusted friends” — it can exfiltrate your SSH keys.

stiff adder
#

let's fix critical first

openclaw sandbox list

🦞 OpenClaw 2026.3.2 (85377a2) — If you're lost, run doctor; if you're brave, run prod; if you're wise, run tests.

No sandbox containers found.
Total: 0 (0 running)

hybrid shardBOT
#

Rebuild question first:

  • Changing openclaw.json (tool allowlists / sandbox mode / bindings): no Docker rebuild.
  • Changing the Dockerfile / sandbox image contents: yes rebuild and you need to force OpenClaw to recreate the sandbox container to pick up the new image/config:
openclaw sandbox recreate --agent relationships

Now the critical part: .ssh being readable is a “stop everything” red flag.
But the thing that doesn’t add up is: you’re saying you’re sandboxed, yet:

openclaw sandbox list → “No sandbox containers found.”

That usually means one of these is true:

A) You’re not actually running in a Docker sandbox (tools are running on host)

#

This would explain why /home/exedev/.ssh is readable.

Quick proof: run this from the relationships session using exec and paste output:

test -f /.dockerenv && echo IN_DOCKER || echo ON_HOST
cat /proc/1/cgroup | head

If it says ON_HOST, you’re not sandboxed in practice.

B) Docker sandbox containers exist, but the CLI is looking at a different state dir/registry

sandbox list reads the registry at $OPENCLAW_STATE_DIR/sandbox/containers.json. If your CLI and your daemon are using different profiles/state dirs, the CLI can show “0” even though containers exist.

Better ground-truth (bypasses the registry):

#
docker ps -a --filter label=openclaw.sandbox=1 --format '{{.Names}}\t{{.Status}}\t{{.Image}}'

If that shows containers, we can docker inspect them to see exactly what’s mounted (and why /home/exedev/.ssh is visible).

C) You changed the sandbox allowlist, but the running daemon didn’t load it

Your pasted config shows you did add relationships.tools.sandbox.tools.allow with message, so “message tool not available in this sandbox” strongly suggests the daemon is still on old config / old session snapshot.

Check:

openclaw gateway status

…and make sure Config (service) is the file you edited. If it isn’t, fix that mismatch first, then restart.

#

If you paste the output of:

  1. docker ps -a --filter label=openclaw.sandbox=1 …
  2. openclaw gateway status (just the config paths section)

we can lock down why .ssh is exposed and why message still isn’t showing up.

stiff adder
#

exedev@diet-poke:~$ openclaw sandbox recreate --agent relationships

🦞 OpenClaw 2026.3.2 (85377a2) — If you're lost, run doctor; if you're brave, run prod; if you're wise, run tests.

No containers found matching the criteria.

the docker file is in somewhere in workspace stored

hybrid shardBOT
#

“No sandbox containers found” (both sandbox list and sandbox recreate) means one of two things:

  1. No sandbox container has ever been created yet (they’re lazy-created the first time a sandboxed tool runs), or
  2. Your exec you used to check /home/exedev/.ssh was not actually running in the sandbox (it ran on the host), so no container/registry entry was created.

Given you were able to browse /home/exedev/.ssh, I strongly suspect #2.

Quick confirm (this matters)

Run this in a context where you believe you’re sandboxed, using exec:

test -f /.dockerenv && echo IN_DOCKER || echo ON_HOST
cat /proc/1/cgroup | head -n 3
  • If it prints ON_HOST, your “sandbox” checks were actually host exec → explains .ssh access and why no containers exist.
  • If it prints IN_DOCKER, then a container should exist and sandbox list should show it (unless state-dir mismatch).
#

Force exec to be sandboxed (so we can reproduce properly)

In config, set for the relationships agent:

{
  agents: {
    list: [
      {
        id: "relationships",
        tools: { exec: { host: "sandbox" } }
      }
    ]
  }
}

Restart gateway, then run any trivial exec (echo hi). After that:

#
openclaw sandbox list --json
docker ps -a --filter label=openclaw.sandbox=1

At that point we can inspect mounts and verify whether /home/exedev or .ssh is actually being mounted (it should not be).

Rebuild answer

  • Config-only changes: no rebuild
  • Dockerfile/image changes: rebuild + openclaw sandbox recreate ... (once containers exist)

If you paste the output of the IN_DOCKER/ON_HOST check + docker ps -a --filter label=openclaw.sandbox=1, we can lock down why .ssh was visible and get you back to a real sandbox boundary.

stiff adder
#

the docker is not getting created or is probably not visible. i don't know if openclaw is able to search for my docker file and then create it. if you can see my config, i changed it to something else like my new docker file.

hybrid shardBOT
#

OpenClaw does not “search for a Dockerfile” or build it for you.

What it does is:

  1. Read agents.*.sandbox.docker.image (an image tag)
  2. Run docker create … <that-image> sleep infinity
  3. Start/reuse that container only when a sandboxed tool actually runs

So:

  • openclaw sandbox explain → only computes policy (does not create anything)
  • openclaw sandbox recreate … → only removes existing containers (does not create anything)
  • If you haven’t triggered a sandboxed tool run (typically exec), you can have 0 containers, which is exactly what you’re seeing.

What to do

  1. Make sure the image exists locally (on the same Docker daemon the gateway uses):
#
docker image inspect openclaw-sandbox:relationships-agent >/dev/null && echo OK
  1. Force a sandboxed tool run from the relationships agent (e.g. ask it to run exec: echo sandbox-test).

  2. Immediately check for created containers (this bypasses OpenClaw’s registry):

docker ps -a --filter label=openclaw.sandbox=1 --format '{{.Names}}\t{{.Status}}\t{{.Image}}'

If still nothing appears, then the gateway process likely can’t run Docker (PATH/permissions/DOCKER_HOST). In that case, grab:

openclaw logs --follow
#

…and trigger that same exec again; we should see an explicit Docker/sandbox error in the logs.

Also: if you’re currently able to browse /home/exedev/.ssh, that strongly suggests your commands are still running on the host (i.e. sandbox isn’t actually in effect yet). Until we see a real openclaw.sandbox=1 container, treat this as not sandboxed.

stiff adder
#

docker images

REPOSITORY TAG IMAGE ID CREATED SIZE
openclaw-sandbox relationships-agent 21937791f12a 5 hours ago 1.6GB
<none> <none> 5dbde82fc58a 5 hours ago 1.6GB
<none> <none> 58972ef3df1d 7 hours ago 904MB
debian bookworm-slim 61cd928eb3ec 10 days ago 74.8MB
openclaw-sandbox bookworm-slim 61cd928eb3ec 10 days ago 74.8MB

nothing in docker ps -a --filter label=openclaw.sandbox=1 --format '{{.Names}}\t{{.Status}}\t{{.Image}}'

hybrid shardBOT
#

Good: the image exists locally (openclaw-sandbox:relationships-agent). If no containers show up with:

docker ps -a --filter label=openclaw.sandbox=1

then OpenClaw has simply never successfully run a sandboxed tool yet (or it’s still running exec on the host).

Two key clarifications:

  1. OpenClaw will not build your Dockerfile.
    It only knows the image tag from config. If the tag isn’t present it will try docker pull, not docker build. You already built it, so you’re good there.

  2. Containers are created lazily on the first sandboxed tool call (usually exec).
    sandbox explain/sandbox list/sandbox recreate won’t create one.

Force a sandboxed exec (so we can see a container)

#

Make sure the relationships agent has exec defaulting to sandbox, then trigger an exec. Easiest is to set this in config:

agents: {
  list: [
    { id: "relationships", tools: { exec: { host: "sandbox" } } }
  ]
}

Restart gateway, then run (from your shell):

openclaw agent --agent relationships --message "Use the exec tool to run: echo sandbox-test && test -f /.dockerenv && echo IN_DOCKER || echo ON_HOST"

Immediately after, check:

#
docker ps -a --filter label=openclaw.sandbox=1 --format '{{.Names}}\t{{.Status}}\t{{.Image}}'

If it still shows nothing

Then the gateway process isn’t able to invoke Docker (PATH/permissions/DOCKER_HOST). In that case, the agent run above should have produced an error, and the gateway logs will have the Docker failure. Grab:

openclaw logs --follow | rg -i "sandbox|docker"

Paste the first Docker-related error you see.

Once we get one openclaw.sandbox=1 container to appear, we can inspect its mounts and confirm whether /home/exedev/.ssh is actually exposed (it really shouldn’t be).

stiff adder
#

but the relationship agent can't access anything now, so its going good

hybrid shardBOT
#

That log paste is the smoking gun:

exec failed: exec host=sandbox is configured, but sandbox runtime is unavailable for this session.

If sandbox runtime is “unavailable”, OpenClaw will not create any Docker container, and docker ps … label=openclaw.sandbox=1 will stay empty. This is why everything looks “not getting created”.

Why this happens (most common)

You configured exec.host=sandbox, but the session you’re running in is not actually sandboxed (sandbox mode is off / doesn’t apply to that session key).

Two easy ways to get into that state:

  1. You set agents.defaults.sandbox.mode: "non-main" and you’re in the main session (DMs often collapse to main unless you change session.dmScope).
  2. You meant to set agents.list[].sandbox.mode: "all" for relationships, but that block isn’t actually in the config the daemon is using (or it didn’t reload).

Fix path

  1. Verify the daemon is using the config you edited:
#
openclaw gateway status

Look for “Config (service)” path.

  1. Ensure relationships has sandbox enabled explicitly:
{
  agents: {
    list: [
      {
        id: "relationships",
        sandbox: { mode: "all", scope: "agent" }
      }
#
    ]
  }
}

(You can keep your workspaceAccess, docker.image, etc — the key is mode: "all".)

  1. Restart gateway:
openclaw gateway restart
  1. Test again with an exec, then check Docker:
#
docker ps -a --filter label=openclaw.sandbox=1

Once sandbox runtime is actually active, you should see the sandbox container appear and exec host=sandbox will stop erroring.

If you paste the openclaw gateway status block showing Config(service)+StateDir plus your current relationships agent sandbox section, I can point out exactly what’s missing/mismatched.

#

If by “can’t access anything” you mean you’re seeing those errors like:

  • exec host=sandbox … sandbox runtime unavailable
  • and exec host=gateway not allowed

…then that’s not a “good sandbox” — that’s a broken tool setup (everything is blocked because sandbox isn’t actually active for the session, and gateway exec is disallowed).

Two sane end states:

A) Safe and functional (what you want for ffmpeg + sending media)

  • Make sure the relationships agent is actually sandboxed at runtime (sandbox.mode: "all" for that agent, and daemon is using that config)
  • Allow only what you need in sandbox tool policy:
    • exec (sandbox) + message (for attachments) + maybe web_fetch

Then exec runs inside Docker (no host .ssh), and message can send /workspace/... files.

B) Locked down / “no tools”

#

If you really want the relationships agent to be tool-less, explicitly deny tools for that agent (so it fails closed cleanly), rather than leaving it in the “sandbox runtime unavailable” limbo.

If you paste your current openclaw sandbox explain --agent relationships now, we can confirm whether you’re in state A (good) or just in the “tools broken” limbo again.

stiff adder
#

exedev@diet-poke:~$ docker ps -a --filter label=openclaw.sandbox=1
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
097deaf8f2dc openclaw-sandbox:relationships-agent "sleep infinity" 32 seconds ago Up 32 seconds openclaw-sbx-agent-relationships-1b0b37cd
exedev@diet-poke:~$ openclaw sandbox explain --agent relationships

🦞 OpenClaw 2026.3.2 (85377a2) — We ship features faster than Apple ships calculator updates.

Effective sandbox:
agentId: relationships
sessionKey: agent:relationships:main
mainSessionKey: agent:relationships:main
runtime: sandboxed
mode: all scope: agent perSession: false
workspaceAccess: none workspaceRoot: /home/exedev/.openclaw/sandboxes

Sandbox tool policy:
allow (default): exec, process, read, write, edit, apply_patch, image, sessions_list, sessions_history, sessions_send, sessions_spawn, subagents, session_status
deny (default): browser, canvas, nodes, cron, gateway, telegram, whatsapp, discord, irc, googlechat, slack, signal, imessage

Elevated:
enabled: false
channel: (unknown)
allowedByConfig: false
failing gates: enabled (tools.elevated.enabled)

Fix-it:

  • agents.defaults.sandbox.mode=off
  • agents.list[].sandbox.mode=off
  • tools.sandbox.tools.allow
  • tools.sandbox.tools.deny
  • agents.list[].tools.sandbox.tools.allow
  • agents.list[].tools.sandbox.tools.deny
  • tools.elevated.enabled

Docs: docs.openclaw.ai/sandbox

hybrid shardBOT
#

Nice — this is the first “healthy” state we’ve seen:

  • Docker container exists (openclaw-sbx-agent-relationships-…)
  • runtime: sandboxed
  • workspaceAccess: none (so it should not be able to see /home/exedev/.ssh anymore)

Now the remaining blocker is exactly what sandbox explain shows:

sandbox tool policy allow (default) … no message

So the message tool is still blocked in sandbox.

Fix: allow message in the relationships sandbox tool allowlist

Add (per-agent) in ~/.openclaw/openclaw.json:

#
{
  agents: {
    list: [
      {
        id: "relationships",
        tools: {
          sandbox: {
            tools: {
              allow: [
                "group:runtime",
                "group:fs",
                "group:sessions",
                "group:messaging",
                "image",
                "subagents",
#
                "web_fetch"
              ]
            }
          }
        }
      }
    ]
  }
}

(Using group:* avoids you having to copy/paste the whole default list; but note this replaces the default allowlist, so include the groups/tools you still need.)

Then restart the gateway (or start a new session) and re-run:

#
openclaw sandbox explain --agent relationships

You should see message effectively allowed.

Then test sending the sandbox file

Inside the relationships session, use message with:

  • action: "send"
  • media: "/workspace/media/pigeon1.mp4"

That’s the “Option A” path that will work with sandbox-produced files.

stiff adder
#

exedev@diet-poke:~$ openclaw sandbox explain --agent relationships

🦞 OpenClaw 2026.3.2 (85377a2) — We ship features faster than Apple ships calculator updates.

Effective sandbox:
agentId: relationships
sessionKey: agent:relationships:main
mainSessionKey: agent:relationships:main
runtime: sandboxed
mode: all scope: agent perSession: false
workspaceAccess: none workspaceRoot: /home/exedev/.openclaw/sandboxes

Sandbox tool policy:
allow (default): exec, process, read, write, edit, apply_patch, image, sessions_list, sessions_history, sessions_send, sessions_spawn, subagents, session_status
deny (default): browser, canvas, nodes, cron, gateway, telegram, whatsapp, discord, irc, googlechat, slack, signal, imessage

Elevated:
enabled: false
channel: (unknown)
allowedByConfig: false
failing gates: enabled (tools.elevated.enabled)

Fix-it:

  • agents.defaults.sandbox.mode=off
  • agents.list[].sandbox.mode=off
  • tools.sandbox.tools.allow
  • tools.sandbox.tools.deny
  • agents.list[].tools.sandbox.tools.allow
  • agents.list[].tools.sandbox.tools.deny
  • tools.elevated.enabled

Docs: docs.openclaw.ai/sandbox

now i want to enable the workspace sharing

hybrid shardBOT
#

To “share the real agent workspace” with the sandbox, you want workspaceAccess.

Right now you’re on:

  • workspaceAccess: none → sandbox uses its own workspace under ~/.openclaw/sandboxes/...

Enable workspace sharing (relationships agent)

In ~/.openclaw/openclaw.json, under the relationships agent:

{
  agents: {
    list: [
      {
        id: "relationships",
        // (workspace: ".../workspace-relationships" stays as-is)
#
        sandbox: {
          mode: "all",
          scope: "agent",
          workspaceAccess: "rw" // or "ro"
        }
      }
    ]
  }
}
  • Use rw if you want the agent to write memory/files back into its workspace.
  • Use ro if you only want it to read the workspace (safer; but write/edit/apply_patch effectively become unusable).

Important: recreate the sandbox container

Mounts are set at container creation time, so you must recreate it:

#
openclaw sandbox recreate --agent relationships

Then verify:

openclaw sandbox explain --agent relationships

You should see workspaceAccess: rw and /workspace inside the container will correspond to the relationships workspace.

Security note: with workspaceAccess: rw, anything in that workspace is accessible to the sandboxed tools—so keep secrets out of workspace-relationships if this agent is exposed to untrusted input.

stiff adder
#

the workspace route is wrong i think

🦞 OpenClaw 2026.3.2 (85377a2) — I'm the reason your shell history looks like a hacker-movie montage.

Effective sandbox:
agentId: relationships
sessionKey: agent:relationships:main
mainSessionKey: agent:relationships:main
runtime: sandboxed
mode: all scope: agent perSession: false
workspaceAccess: rw workspaceRoot: /home/exedev/.openclaw/sandboxes

Sandbox tool policy:
allow (agent): exec, process, read, write, edit, apply_patch, sessions_list, sessions_history, sessions_send, sessions_spawn, subagents, session_status, message, image, web_fetch
deny (default): browser, canvas, nodes, cron, gateway, telegram, whatsapp, discord, irc, googlechat, slack, signal, imessage

Elevated:
enabled: false
channel: (unknown)
allowedByConfig: false
failing gates: enabled (tools.elevated.enabled)

Fix-it:

  • agents.defaults.sandbox.mode=off
  • agents.list[].sandbox.mode=off
  • tools.sandbox.tools.allow
  • tools.sandbox.tools.deny
  • agents.list[].tools.sandbox.tools.allow
  • agents.list[].tools.sandbox.tools.deny
  • tools.elevated.enabled

Docs: docs.openclaw.ai/sandbox

hybrid shardBOT
#

That output actually looks consistent — the confusing bit is just the label.

  • workspaceRoot: /home/exedev/.openclaw/sandboxes is the sandbox workspace root setting (used when workspaceAccess: "none" / "ro").
  • With workspaceAccess: rw, the sandbox’s effective working directory becomes your agent workspace, and it gets mounted into the container at /workspace. sandbox explain doesn’t print that resolved workspaceDir, only the configured workspaceRoot.

So “workspaceRoot looks wrong” is expected here.

Quick sanity checks (do these from the relationships agent via exec)

pwd
ls -la /workspace | head
ls -la /agent 2>/dev/null || echo "/agent not mounted (expected for workspaceAccess=rw)"

You should see:

  • pwd → /workspace
  • /workspace contents match ~/.openclaw/workspace-relationships on the host
#

And now you’re unblocked for media

Your sandbox tool policy now shows message allowed (allow (agent): … message …), so you can do Option A:

  • message tool → action: "send" → media: "/workspace/media/pigeon1.mp4"

Inline MEDIA:/workspace/... will still fail (gateway reads on host), but the message tool will normalize the sandbox path correctly.

stiff adder
#

what will be the alternate if i want to access inline media?

hybrid shardBOT
#

Inline MEDIA: is always handled by the gateway on the host (loadWebMedia), and it does not translate sandbox/container paths. So:

  • MEDIA:/workspace/... will never work (unless OpenClaw adds special mapping).

If you really want inline MEDIA:, the “alternate” is: make the path you put after MEDIA: a host-visible path under an allowed root.

Practical options:

  1. Inline full host path (works today)
    Since you’ve enabled workspaceAccess: rw, the file exists on the host workspace too, so you can emit e.g.:

MEDIA:/home/exedev/.openclaw/workspace-relationships/media/pigeon1.mp4

  1. Write/copy the file into ~/.openclaw/media/... and inline that path
    That directory is always allowlisted for local media reads. (Getting the file there from a sandbox usually means either a bind-mount to that dir, or a host-side copy step.)

  2. Use a URL

#

Upload somewhere and do MEDIA:https://…

If your goal is “don’t leak host paths / usernames”, there isn’t a clean inline-only solution right now — the message tool is the path that avoids that because it can accept /workspace/... and does the sandbox mapping internally.

stiff adder
#

the message tool is looking for media on the host at /home/exedev/.openclaw/sandboxes/agent-relationships-1b0b37cd/media/... but we only have it in /workspace/media/ inside the container

hybrid shardBOT
#

If the sandbox is working correctly, those two paths are the same storage:

  • container: /workspace/media/...
  • host: /home/exedev/.openclaw/sandboxes/agent-relationships-1b0b37cd/media/...

/workspace in the OpenClaw sandbox container is a bind-mount of that host sandbox dir. So if the file truly exists in /workspace/media inside the OpenClaw sandbox container, it must exist on the host path too.

What usually causes the “it’s only in /workspace inside the container” confusion is: the file was created in a different container (e.g. your manual docker run … openclaw-sandbox:relationships-agent), not in the OpenClaw-managed container (openclaw-sbx-agent-relationships-1b0b37cd).

Quick verification (no guesswork)

Run these on the host:

# 1) Check inside the *OpenClaw sandbox container*
docker exec openclaw-sbx-agent-relationships-1b0b37cd sh -lc 'ls -la /workspace/media && stat /workspace/media/pigeon1.mp4'
#
# 2) Check the corresponding host dir
ls -la /home/exedev/.openclaw/sandboxes/agent-relationships-1b0b37cd/media
  • If the container has the file but the host dir doesn’t: that would be a serious mount bug (very unlikely; we’ll inspect docker inspect … .Mounts next).
  • If the container doesn’t have the file: you generated it somewhere else — regenerate it via the relationships agent’s exec (in the sandboxed session) so it lands in the correct /workspace.

Once the file is in the OpenClaw sandbox container’s /workspace, the message tool should be able to attach it (because it reads from the host sandbox root it already knows about).

stiff adder
#

docker exec openclaw-sbx-agent-relationships-1b0b37cd sh -lc 'ls -la /workspace/media && stat /workspace/media/pigeon1.mp4'
total 1716
drwxrwxr-x 4 1000 1000 4096 Mar 5 07:00 .
drwxr-xr-x 17 1000 1000 4096 Mar 5 11:54 ..
drwxrwxr-x 2 1000 1000 4096 Mar 5 03:31 inbound
drwxrwxr-x 3 1000 1000 4096 Mar 5 06:43 memes
-rw-r--r-- 1 1000 1000 372079 Mar 5 06:24 pigeon1.mp4
-rw-r--r-- 1 1000 1000 318701 Mar 5 03:56 pigeon2.mp4
-rw-r--r-- 1 1000 1000 318701 Mar 5 06:29 pigeon_aMV57o6.mp4
-rw-r--r-- 1 1000 1000 372079 Mar 5 06:29 pigeon_agmw9dv.mp4
-rw-rw-r-- 1 1000 1000 353815 Feb 25 15:50 yo.mp3
File: /workspace/media/pigeon1.mp4
Size: 372079 Blocks: 728 IO Block: 4096 regular file
Device: 254,0 Inode: 409240 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 1000/ UNKNOWN) Gid: ( 1000/ UNKNOWN)
Access: 2026-03-05 07:00:23.486335931 +0000
Modify: 2026-03-05 06:24:53.157545164 +0000
Change: 2026-03-05 07:00:03.674160249 +0000
Birth: 2026-03-05 03:56:47.090135086 +0000

exedev@diet-poke:~$ # 2) Check the corresponding host dir
ls -la /home/exedev/.openclaw/sandboxes/agent-relationships-1b0b37cd/media
ls: cannot access '/home/exedev/.openclaw/sandboxes/agent-relationships-1b0b37cd/media': No such file or directory

hybrid shardBOT
#

In a correct OpenClaw sandbox, /workspace inside the container is a bind mount of a host directory. So the fact that:

  • file exists in container: /workspace/media/pigeon1.mp4
  • but host path you guessed doesn’t exist: ~/.openclaw/sandboxes/agent-relationships-…/media

means one of these is true:

  1. /workspace is not actually the mount destination OpenClaw used (so you’re writing to a normal container dir), or
  2. the host “sandbox root” directory is somewhere else (profile/state-dir mismatch), and you’re looking in the wrong place.

Let’s stop guessing and inspect the container mounts directly:

docker inspect openclaw-sbx-agent-relationships-1b0b37cd \
  --format '{{range .Mounts}}{{println .Type .Source "->" .Destination}}{{end}}'
#
docker inspect openclaw-sbx-agent-relationships-1b0b37cd \
  --format 'workdir={{.Config.WorkingDir}}'

What you want to see is something like:

  • bind /some/host/path -> /workspace
  • workdir=/workspace

Then check that exact host path:

ls -la /some/host/path/media

That /some/host/path is what the message tool’s sandboxRoot will use. Once the mount is correct, message with media: "/workspace/media/pigeon1.mp4" will work.

#

Paste the two docker inspect --format … outputs and we can tell immediately which side is wrong (mount destination vs state-dir mismatch).

stiff adder
#

exedev@diet-poke:~$ docker inspect openclaw-sbx-agent-relationships-1b0b37cd
--format '{{range .Mounts}}{{println .Type .Source "->" .Destination}}{{end}}'
bind /home/exedev/.openclaw/workspace-relationships -> /workspace

exedev@diet-poke:~$ docker inspect openclaw-sbx-agent-relationships-1b0b37cd
--format 'workdir={{.Config.WorkingDir}}'
workdir=/workspace

hybrid shardBOT
#

Great — this explains everything.

Your sandbox container has:

  • bind mount: /home/exedev/.openclaw/workspace-relationships → /workspace
  • workdir: /workspace

So /workspace/media/pigeon1.mp4 inside the container is literally the host file:

/home/exedev/.openclaw/workspace-relationships/media/pigeon1.mp4

That’s why looking under ~/.openclaw/sandboxes/agent-relationships-… didn’t work — in your current setup, you’re not using a separate sandbox workspace; you’re using the real agent workspace mounted into the container.

What this means for sending

  • Inline MEDIA:/workspace/... will still fail (gateway reads on host; /workspace is container path).
  • Inline MEDIA:/home/exedev/.openclaw/workspace-relationships/media/pigeon1.mp4 will work.
  • Message tool should work with either:
#
  • media: "/workspace/media/pigeon1.mp4" (preferred in sandboxed session), or
    • media: "/home/exedev/.openclaw/workspace-relationships/media/pigeon1.mp4"

If the message tool still errors, paste the exact failure line now — at this point it should be something concrete like “file not found” or a size/media cap, not a path-mapping mystery.

stiff adder
#

FROM debian:13

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y
build-essential
curl
git
ca-certificates
python3-pip
python3-venv
wget
ffmpeg
&& rm -rf /var/lib/apt/lists/*

Node 24

RUN curl -fsSL https://deb.nodesource.com/setup_24.x | bash -
&& apt-get install -y nodejs
&& rm -rf /var/lib/apt/lists/*

WORKDIR /workspace

CMD ["/bin/bash"]

hybrid shardBOT
#

That paste is consistent with a sandbox config mismatch:

  • The message tool is trying to read from sandboxRoot on the host:
    /home/exedev/.openclaw/sandboxes/agent-relationships-1b0b37cd/...
  • But your container’s /workspace is (now) mounted from:
    /home/exedev/.openclaw/workspace-relationships (per your docker inspect)

So at the time of those errors, the gateway believed the sandbox workspace was under ~/.openclaw/sandboxes/..., but the running container was using a different mount source (or vice‑versa). OpenClaw even logged the fix repeatedly:

“Sandbox config changed … Recreate to apply: openclaw sandbox recreate --agent relationships”

Fix

  1. Decide which mode you want:
  • workspaceAccess: none → /workspace should be backed by ~/.openclaw/sandboxes/<slug>/
  • workspaceAccess: rw → /workspace should be backed by ~/.openclaw/workspace-relationships/
  1. Set that in config, then recreate the sandbox container:
#
openclaw sandbox recreate --agent relationships
  1. After recreate, confirm the mount matches the mode:
docker inspect openclaw-sbx-agent-relationships-1b0b37cd \
  --format '{{range .Mounts}}{{println .Type .Source "->" .Destination}}{{end}}'

Once the mount and sandboxRoot agree, message with media: "/workspace/media/pigeon1.mp4" will stop looking in the wrong host directory and the “not found” errors go away.

#

That Dockerfile is totally fine for a custom sandbox image (and adding WORKDIR /workspace is harmless), but it won’t fix the host-path vs /workspace mismatch by itself.

A couple of notes so expectations are correct:

  • OpenClaw doesn’t run your CMD. It creates the container as … <image> sleep infinity, so your CMD ["/bin/bash"] is basically ignored.
  • OpenClaw also sets the workdir via docker create --workdir … (default /workspace). So WORKDIR /workspace is redundant, but fine.

The thing that does control where /workspace maps is OpenClaw sandbox config + whether you recreated the container after changing it:

  • workspaceAccess: none → /workspace should map to a host dir under ~/.openclaw/sandboxes/<slug>/
  • workspaceAccess: rw → /workspace maps to your agent workspace (e.g. ~/.openclaw/workspace-relationships/)

And any time you change workspaceAccess / docker settings, you must:

openclaw sandbox recreate --agent relationships
#

After that, docker inspect … .Mounts should match the mode you intend, and then message with media: "/workspace/media/pigeon1.mp4" will work consistently.

If you tell me which mode you want right now (none vs rw), I can give you the minimal config block for relationships.sandbox to lock it in.

stiff adder
#

{
"id": "relationships",
"workspace": "/home/exedev/.openclaw/workspace-relationships",
"memorySearch": {
"extraPaths": [
"/home/exedev/.openclaw/knowledge",
"/home/exedev/.openclaw/workspace-relationships",
"/home/exedev/.openclaw/workspace-relationships/memory"
]
},
"sandbox": {
"mode": "all",
"scope": "agent",
"workspaceAccess": "rw"
},
"tools": {
"sandbox": {
"tools": {
"allow": [
"group:runtime",
"group:fs",
"group:sessions",
"group:messaging",
"image",
"subagents",
"web_fetch"
]
}
},
"exec": {
"host": "sandbox"
}
}
},

hybrid shardBOT
#

That relationships agent block looks correct for the “safe + functional” setup:

  • sandbox.mode: "all" + scope: "agent" → it will actually create/use a Docker sandbox for that agent
  • workspaceAccess: "rw" → /workspace in the container is your real workspace-relationships (so files written there exist on the host too)
  • tools.sandbox.tools.allow includes group:messaging → the message tool is allowed in sandbox
  • tools.exec.host: "sandbox" → exec runs in the container

Two last steps to make it actually take effect cleanly:

  1. Recreate the sandbox container (mounts + tool policy snapshots depend on the container lifecycle):
openclaw sandbox recreate --agent relationships
  1. Test media send via message (this is the one that will work with /workspace/...):
#
  • action: "send"
  • media: "/workspace/media/pigeon1.mp4" (or filePath/path)
  • omit target if you’re sending back to the same chat; it can infer

Inline MEDIA:/workspace/... will still fail (host can’t read container paths), but with this config the message tool path should work reliably.

If after the recreate it still fails, paste the exact LocalMediaAccessError line and the result of:

docker inspect <container> --format '{{range .Mounts}}{{println .Source "->" .Destination}}{{end}}'
stiff adder
hybrid shardBOT
#

Got it — that paste shows what’s happening:

  • You recreated the sandbox container (Removed openclaw-sbx-agent-relationships-…).
  • After recreate + /new, the agent is in a fresh sandbox workspace (or at least a workspace that doesn’t contain your old media/ files).
  • Then message tries to read from the host-side sandboxRoot (e.g. ~/.openclaw/sandboxes/agent-relationships-…/media/pigeon1.mp4) and it’s not there, so you get Local media file not found.

So it’s not that message can’t map /workspace/... — it is mapping. The file just isn’t in the workspace that is currently mounted.

Fix (works regardless of rw vs none)

  1. Ask Docker what host directory is mounted at /workspace:
docker inspect openclaw-sbx-agent-relationships-1b0b37cd \
  --format '{{range .Mounts}}{{if eq .Destination "/workspace"}}{{println .Source}}{{end}}{{end}}'
  1. Copy your mp4 into that host directory’s media/ folder:
#
WORKSPACE_SRC="$(docker inspect openclaw-sbx-agent-relationships-1b0b37cd --format '{{range .Mounts}}{{if eq .Destination \"/workspace\"}}{{.Source}}{{end}}{{end}}')"
mkdir -p "$WORKSPACE_SRC/media"
cp /home/exedev/.openclaw/workspace-relationships/media/pigeon1.mp4 "$WORKSPACE_SRC/media/"
  1. Now message with media: "/workspace/media/pigeon1.mp4" will work (because the file is actually in the mounted workspace).

Choosing your mode

  • If you want the sandbox to always see your existing media under workspace-relationships, keep workspaceAccess: "rw" (mounts that workspace into /workspace).
  • If you want stronger isolation (workspaceAccess: "none"), you need to “seed” media by copying it into the sandbox workspace as above.

If you paste the single-line output of step (1) (the /workspace mount source), I can tell you which mode you’re currently in and whether that Read-only file system line is coming from workspaceAccess: "ro" or a permissions issue on the mounted dir.

stiff adder
#

exedev@diet-poke:~$ docker inspect openclaw-sbx-agent-relationships-1b0b37cd --format '{{range .Mounts}}{{if eq .Destination "/workspace"}}{{println .Source}}{{end}}{{end}}'
/home/exedev/.openclaw/sandboxes/agent-relationships-1b0b37cd

hybrid shardBOT
#

Perfect — that output tells us exactly where the gateway expects “sandbox local files” to live on the host:

/home/exedev/.openclaw/sandboxes/agent-relationships-1b0b37cd

So if you want message to successfully send media: "/workspace/media/pigeon1.mp4" while you’re in workspaceAccess: none, you need the file to exist at:

/home/exedev/.openclaw/sandboxes/agent-relationships-1b0b37cd/media/pigeon1.mp4

Do this on the host:

mkdir -p /home/exedev/.openclaw/sandboxes/agent-relationships-1b0b37cd/media
cp /home/exedev/.openclaw/workspace-relationships/media/pigeon1.mp4 \
   /home/exedev/.openclaw/sandboxes/agent-relationships-1b0b37cd/media/

Then sanity check:

#
docker exec openclaw-sbx-agent-relationships-1b0b37cd ls -la /workspace/media/pigeon1.mp4

After that, the message tool with:

  • action: "send"
  • media: "/workspace/media/pigeon1.mp4"

should stop throwing “Local media file not found”.

(And this is the “safe” pattern for untrusted users: keep workspaceAccess: none, and only copy the specific media you’re okay exposing into the sandbox workspace.)

stiff adder
#

no i want the whole workspace access

hybrid shardBOT
#

Cool — then you want workspaceAccess: "rw" for the relationships agent (so /workspace inside the sandbox is the real workspace-relationships on the host).

Do this

In your relationships agent config:

sandbox: {
  mode: "all",
  scope: "agent",
  workspaceAccess: "rw"
}

Then recreate the sandbox container (mounts only change on recreate):

#
openclaw sandbox recreate --agent relationships

After the next use of the agent, confirm the mount:

docker inspect openclaw-sbx-agent-relationships-1b0b37cd \
  --format '{{range .Mounts}}{{println .Type .Source "->" .Destination}}{{end}}'

You should see:

bind /home/exedev/.openclaw/workspace-relationships -> /workspace

Security note (since you mentioned “untrusted friends” earlier)

“Whole workspace access” means the sandboxed tools can read/write everything in workspace-relationships. So keep that workspace clean (no SSH keys, no tokens, no other projects). If you do that, workspaceAccess: rw is a reasonable setup.

stiff adder
#

docker inspect openclaw-sbx-agent-relationships-1b0b37cd
--format '{{range .Mounts}}{{println .Type .Source "->" .Destination}}{{end}}'
bind /home/exedev/.openclaw/sandboxes/agent-relationships-1b0b37cd -> /workspace

hybrid shardBOT
#

That mount output means the container was created with workspaceAccess ≠ rw (it’s using the sandbox workspace under ~/.openclaw/sandboxes/...).

To get:

bind /home/exedev/.openclaw/workspace-relationships -> /workspace

you need both:

  1. Config actually set to workspaceAccess: "rw" for relationships
  2. The container recreated after that change

Do this sequence (order matters)

  1. Confirm what OpenClaw thinks the config is:
#
openclaw sandbox explain --agent relationships

Look at the workspaceAccess: line — if it still says none, the daemon/config hasn’t picked up your change.

  1. After you set workspaceAccess: "rw" in the right config file, restart gateway:
openclaw gateway restart
  1. Remove the existing sandbox container:
openclaw sandbox recreate --agent relationships
#
  1. Trigger the agent once (so it recreates the container), then inspect again:
docker inspect openclaw-sbx-agent-relationships-1b0b37cd \
  --format '{{range .Mounts}}{{println .Type .Source "->" .Destination}}{{end}}'

If after step (1) it still reports workspaceAccess: none, paste that sandbox explain output — that’ll tell us whether the config edit is in the wrong place/profile or not being loaded.

stiff adder
#

🦞 OpenClaw 2026.3.2 (85377a2) — Deployed locally, trusted globally, debugged eternally.

Effective sandbox:
agentId: relationships
sessionKey: agent:relationships:main
mainSessionKey: agent:relationships:main
runtime: sandboxed
mode: all scope: agent perSession: false
workspaceAccess: rw workspaceRoot: /home/exedev/.openclaw/sandboxes

Sandbox tool policy:
allow (agent): exec, process, read, write, edit, apply_patch, sessions_list, sessions_history, sessions_send, sessions_spawn, subagents, session_status, message, image, web_fetch
deny (default): browser, canvas, nodes, cron, gateway, telegram, whatsapp, discord, irc, googlechat, slack, signal, imessage

Elevated:
enabled: false
channel: (unknown)
allowedByConfig: false
failing gates: enabled (tools.elevated.enabled)

Fix-it:

  • agents.defaults.sandbox.mode=off
  • agents.list[].sandbox.mode=off
  • tools.sandbox.tools.allow
  • tools.sandbox.tools.deny
  • agents.list[].tools.sandbox.tools.allow
  • agents.list[].tools.sandbox.tools.deny
  • tools.elevated.enabled

Docs: docs.openclaw.ai/sandbox

hybrid shardBOT
#

Nice — config side is now correct (workspaceAccess: rw, message allowed, etc).

If docker inspect is still showing:

bind /home/exedev/.openclaw/sandboxes/agent-relationships-… -> /workspace

that just means you’re looking at an old container created before the config change. Mounts don’t update in-place.

Do:

openclaw sandbox recreate --agent relationships

Then trigger the relationships agent once (any inbound message or openclaw agent --agent relationships --message "hi"), and re-check:

#
docker inspect openclaw-sbx-agent-relationships-1b0b37cd \
  --format '{{range .Mounts}}{{println .Type .Source "->" .Destination}}{{end}}'

Now you should see:

bind /home/exedev/.openclaw/workspace-relationships -> /workspace

After that, message with media: "/workspace/media/pigeon1.mp4" should stop trying the sandboxes path.

stiff adder
hybrid shardBOT
#

Looked through tr2 — the failures you’re seeing there are all consistent with one simple fact at the time:

The sandbox container that existed then was still using the sandboxes workspace

So message correctly normalized /workspace/media/pigeon1.mp4 → host sandboxRoot:

/home/exedev/.openclaw/sandboxes/agent-relationships-1b0b37cd/media/pigeon1.mp4

…and then failed with not-found because that fresh sandbox workspace didn’t have the pigeon files.

That’s why the bot says “folder doesn’t exist yet / upload it”.

What you want (whole workspace access)

If you want the pigeons that already live in:

/home/exedev/.openclaw/workspace-relationships/media/…

then you must ensure the current sandbox container mount is:

#

bind /home/exedev/.openclaw/workspace-relationships -> /workspace

Your latest sandbox explain shows the config is now workspaceAccess: rw (good), but you need to make sure the running container was recreated after that change.

Do this now (one clean cycle)

  1. Remove the current container:
openclaw sandbox recreate --agent relationships
  1. Trigger the relationships agent once (so it recreates the container).

  2. Immediately verify mount:

#
docker inspect openclaw-sbx-agent-relationships-1b0b37cd \
  --format '{{range .Mounts}}{{println .Source "->" .Destination}}{{end}}'

If it still shows the sandboxes path, then the daemon isn’t using the config you think it is (next step would be openclaw gateway status to confirm Config(service) path).

Once the mount is workspace-relationships -> /workspace, message with media: "/workspace/media/pigeon1.mp4" will work without any copying.

stiff adder
#

hey @hybrid shard

hybrid shardBOT
#

That 4fK paste is useful because it shows two separate things getting mixed up in the thread starter.

1) “Sandbox can’t reach gateway because gateway is 127.0.0.1” — only true for the wrong architecture

gateway.bind=loopback only blocks other processes that need to connect over TCP (e.g. “run a whole OpenClaw client/agent inside Docker and have it talk to the gateway”).

OpenClaw’s built-in tool sandboxing is not that: the agent + message sending still happens in the gateway process on the host, and only specific tools (like exec) run in Docker. So sandboxed agents do not need container→gateway network access for message/sessions_send to work.

2) Your actual blocker is still the workspace/mount

Your earlier docker inspect showing:

/home/exedev/.openclaw/sandboxes/agent-relationships-… -> /workspace

means the container you inspected was still created with /workspace pointing at the sandboxRoot, not your workspace-relationships.

If you truly want “whole workspace access”, the next thing to verify is your agent’s configured workspace path (not just workspaceAccess: rw):

  • what is agents.list.relationships.workspace set to?
#

Because with workspaceAccess: rw, /workspace should become whatever relationships.workspace is.

If you paste the relationships agent block from ~/.openclaw/openclaw.json (just that agent), I can tell you exactly why it keeps mounting ~/.openclaw/sandboxes/... instead of ~/.openclaw/workspace-relationships.

(Separate minor oddity: openclaw status reporting “Gateway … unreachable (timeout)” while the service is running is weird — if it persists, run openclaw gateway probe and paste that output.)

#

Your thread starter mixes up two different “Docker” setups:

A) OpenClaw tool-sandboxing (what sandbox.mode=all does)

  • The agent + gateway + messaging live on the host gateway process.
  • Only specific tools (like exec) run inside the Docker container.
  • So the sandbox container does not need to “reach the gateway API” over the network at all.
  • gateway.bind=127.0.0.1 is totally fine here, because nothing inside the container is dialing 127.0.0.1:8081.

If message/sessions_send “don’t work” in this mode, it’s almost always:

  • sandbox tool policy not allowing message, or
  • media path/mount mismatch (/workspace/... file not actually present in the host dir mounted to /workspace), or
  • container not recreated after config changes.

B) Running a separate OpenClaw client/agent inside Docker

  • This does require network access to the gateway.
  • Here loopback bind (127.0.0.1) breaks it unless you bind to LAN/custom and allow the network path.
#

Most of your logs so far match (A), and the failures were “file not found / wrong mount”, not “can’t connect to gateway”.

#

If you want a “thread starter” that’s accurate for OpenClaw’s built-in sandboxing, here’s the corrected version you can paste:


What’s actually happening with sandbox.mode=all (tool sandboxing)

In OpenClaw, sandboxing puts tools (e.g. exec/ffmpeg) inside Docker, but the agent + gateway + channel providers (WhatsApp/Telegram) still run on the host gateway process.

So:

  • The Docker container does not need network access to the gateway
  • gateway.bind=127.0.0.1 does not block sandboxed agents from using message / sessions_send
  • When message/media fails in a sandboxed agent, it’s usually:
    • sandbox tool policy (message not allowed), or
    • media path/mount mismatch (/workspace/... doesn’t exist in the host dir mounted to /workspace), or
    • container not recreated after config changes
#

When loopback binding does matter

Only if you run a separate OpenClaw client/agent inside Docker that must connect to the gateway over TCP — then loopback breaks it and you’d need LAN/custom bind.


And no worries on the ping — I’m watching the thread.

stiff adder
#

yeah i read it - tried both:

  1. message tool with filePath parameter
  2. MEDIA:<path> in message body

both fail with same error - tool maps paths to /home/exedev/.openclaw/workspace-relationships/ but sandbox files are at /workspace/.openclaw/workspace-relationships/

the doc method doesn't help if the tool can't resolve the path 🐦

hybrid shardBOT
#

That path mismatch is the giveaway: the tool is resolving correctly — the file is being written to the wrong place inside the sandbox.

What’s happening

Inside the sandbox, your $HOME is effectively /workspace (or your code is writing to ~/.openclaw/...), so you end up creating files under:

  • inside container: /workspace/.openclaw/workspace-relationships/...
  • host equivalent: /home/exedev/.openclaw/workspace-relationships/.openclaw/workspace-relationships/...

But when you send filePath: "media/pigeon1.mp4" (or MEDIA:/home/exedev/.openclaw/workspace-relationships/media/...) OpenClaw looks for:

  • /home/exedev/.openclaw/workspace-relationships/media/...

…which doesn’t exist, because your file is nested under that extra .openclaw/workspace-relationships/.

Fix

Write outputs directly into the workspace root, e.g.:

#
  • ffmpeg output to: media/pigeon1.mp4 (cwd /workspace) or /workspace/media/pigeon1.mp4
  • do not output to ~/.openclaw/... from inside the sandbox

One-time cleanup/move (inside the container):

mkdir -p /workspace/media
mv /workspace/.openclaw/workspace-relationships/media/* /workspace/media/ 2>/dev/null || true

Then sending works with either:

  • message tool: media: "/workspace/media/pigeon1.mp4" (or filePath: "media/pigeon1.mp4")
  • inline: MEDIA:/home/exedev/.openclaw/workspace-relationships/media/pigeon1.mp4

If you paste the exact error line + the exact path you’re passing (MEDIA:... or filePath/media:), I can tell you which of the two (not-found vs not-allowed) you’re hitting.

stiff adder
#

relationship agent is saying

all three fail with same error - tool maps everything to /home/exedev/.openclaw/workspace-relationships/ which isn't accessible

this is a host-side config issue - you need to fix the allowed directories on the gateway 🐦

hybrid shardBOT
#

The relationships agent is (politely) bullshitting you there.

If the tool normalizes to:

/home/exedev/.openclaw/workspace-relationships/...

that path is on the host, and your gateway (systemd --user service) absolutely can read it. That’s the intended design: the gateway reads the file locally to attach it.

What’s almost certainly happening is one of these (and the error text will tell which):

  1. not-found: the file simply isn’t at that host path (common when the sandbox wrote it under the extra prefix like .openclaw/workspace-relationships/... inside /workspace)
  2. path-not-allowed: you’re pointing outside the allowed local roots (less likely if you’re truly under the agent workspace)

Quick reality check (host)

Run:

#
ls -la /home/exedev/.openclaw/workspace-relationships/media/pigeon1.mp4
ls -la /home/exedev/.openclaw/workspace-relationships/.openclaw/workspace-relationships/media/pigeon1.mp4

One of those will exist, and the other won’t — that’ll explain the “same error”.

Don’t “fix allowed directories” yet

Widening allowed roots is the last resort (and a security footgun for untrusted users). The right fix is: make ffmpeg (or whatever) write to /workspace/media/... (or media/...), so the normalized host path actually exists.

If you paste the exact LocalMediaAccessError: ... line (the one that includes code=...), I can tell you immediately whether it’s not-found or path-not-allowed.

stiff adder
#

I don't know who is bullshitting but here it is

sandbox mei pigeon1.mp4 hai at:

  • /workspace/media/pigeon1.mp4 ✓

but tool looks at HOST path: /home/exedev/.openclaw/workspace-relationships/

that host path doesn't exist in sandbox - files are in /workspace/, not /home/exedev/

so either:

  1. copy files from sandbox to host path, OR
  2. fix gateway config to map sandbox paths correctly

you'll need to do one of these bhai 🐦

hybrid shardBOT
#

The missing piece is: the gateway always reads attachments from the host filesystem, not from inside the container. So it must translate /workspace/... → “whatever host dir is mounted at /workspace”.

If things were consistent, then both of these would be true at the same time:

  • in container: /workspace/media/pigeon1.mp4 exists
  • on host: <MOUNT_SOURCE>/media/pigeon1.mp4 exists (same bytes)

So let’s make it concrete

  1. What is /workspace actually mounted from?
docker inspect openclaw-sbx-agent-relationships-1b0b37cd \
  --format '{{range .Mounts}}{{if eq .Destination "/workspace"}}{{println .Source}}{{end}}{{end}}'

Call that output MOUNT_SOURCE.

#
  1. Now check the file on the host in that exact directory:
ls -la "$MOUNT_SOURCE/media/pigeon1.mp4"
  • If that file exists on host, message should be able to attach it (and if it can’t, the error will be path-not-allowed, not “not found”).
  • If that file does not exist on host, then despite what you saw in-container, the file is not actually ending up on the bind mount you think (usually because it was written somewhere else like /workspace/.openclaw/..., or you’re looking at a different container / recreated container).

Key point

Saying “host path /home/exedev/... doesn’t exist in the sandbox” is irrelevant — the sandbox never needs to see that path. The only question is: does the host file exist under the host mount source that backs /workspace?

Paste:

  • the docker inspect mount source line, and
  • the exact LocalMediaAccessError: ... code=... line

…and I’ll tell you exactly which branch you’re in (mismatched mount vs wrong output directory vs allow-roots).

stiff adder
#

docker not available in sandbox

i literally cannot run:

  • docker commands
  • host filesystem access
  • any command outside /workspace/

you need to run those commands on YOUR host terminal, not from here

run this on your host:

docker inspect openclaw-sbx-agent-relationships-1b0b37cd --format '{{range .Mounts}}{{if eq .Destination "/workspace"}}{{println .Source}}{{end}}{{end}}'

then:

ls -la "<MOUNT_SOURCE>/media/pigeon1.mp4"

paste the outputs here 🐦

hybrid shardBOT
#

Yep — those checks have to be run on the host, not from inside the sandbox. I can’t run commands on your machine; you need to run them locally and paste results.

Good news: you already effectively have MOUNT_SOURCE from earlier docker inspect runs. If right now it’s:

/home/exedev/.openclaw/sandboxes/agent-relationships-1b0b37cd

then the decisive host-side check is simply:

ls -la /home/exedev/.openclaw/sandboxes/agent-relationships-1b0b37cd/media/pigeon1.mp4

If that file exists on the host, message media: "/workspace/media/pigeon1.mp4" should work.

If it does not exist on the host but you “see it” in the sandbox, then you’re not actually writing to the bind mount you think you are (wrong output dir, different container, or /workspace not what you expect) — and we’ll chase that with the exact LocalMediaAccessError ... code=... line from the gateway logs.

stiff adder
#

Output of the Docker was the same
/home/exedev/.openclaw/workspace-relationships

hybrid shardBOT
#

Great — if Docker says the /workspace mount source is:

/home/exedev/.openclaw/workspace-relationships

then there is no “sandbox vs host path mismatch” anymore. /workspace/... inside the container is literally that same directory on the host.

Next decisive check on the host:

ls -la /home/exedev/.openclaw/workspace-relationships/media/pigeon1.mp4
stat /home/exedev/.openclaw/workspace-relationships/media/pigeon1.mp4
  • If that file is missing on the host, then it isn’t actually being written to /workspace/media/... (common culprit: it’s being written under /workspace/.openclaw/... instead).
  • If it exists on the host, then message media: "/workspace/media/pigeon1.mp4" should work — and if it still fails, I need the exact gateway error line (LocalMediaAccessError: ... code=...) because then we’re dealing with either allow-roots (path-not-allowed) or a different session/agent context than you think.
stiff adder
#

yes file exists

hybrid shardBOT
#

That log finally nails it:

LocalMediaAccessError: Local media path is not under an allowed directory:
  /home/exedev/.openclaw/workspace-relationships/media/pigeon1.mp4
code=path-not-allowed

So this is not “sandbox can’t reach host” and not “file missing” — it’s the local media allowlist.

Why it happens

The web media loader (src/web/media.ts) has a hardening rule: when it’s using the default localRoots, it will reject paths under the state dir that look like workspace-* (like ~/.openclaw/workspace-relationships/...) unless the caller passes an explicit localRoots list.

That’s exactly your case: workspace-relationships matches the blocked workspace-* pattern.

Fastest fix (recommended)

Put the relationships workspace under the allowed state workspace root, instead of using workspace-relationships:

#
  • change relationships.workspace from
    /home/exedev/.openclaw/workspace-relationships
  • to something like:
    /home/exedev/.openclaw/workspace/relationships

Then pigeon1.mp4 lives under ~/.openclaw/workspace/... which is allowed by default, and both message attachments + inline MEDIA: stop tripping path-not-allowed.

After changing it, do the usual:

openclaw gateway restart
openclaw sandbox recreate --agent relationships

Longer-term (probably a bug)

Given you are in an agent-scoped session, the outbound path should ideally be using getAgentScopedMediaLocalRoots(...) so agent workspaces are allowed. If you want to push this upstream, this log + the path-not-allowed behavior is a solid GitHub issue.

#

Good — then the remaining issue is purely the allowlist (code=path-not-allowed), because ~/.openclaw/workspace-relationships/... matches the blocked workspace-* pattern in the default localRoots hardening.

You’ve got two practical options:

Option A (best): move the relationships workspace under ~/.openclaw/workspace/…

This makes it fall under a default-allowed root.

On the host:

mkdir -p /home/exedev/.openclaw/workspace

# move existing workspace (keeps your media + memory)
mv /home/exedev/.openclaw/workspace-relationships \
   /home/exedev/.openclaw/workspace/relationships
#

Then update ~/.openclaw/openclaw.json:

agents: {
  list: [
    { id: "relationships", workspace: "/home/exedev/.openclaw/workspace/relationships", ... }
  ]
}

Then:

openclaw gateway restart
openclaw sandbox recreate --agent relationships
#

Re-test message media: "/workspace/media/pigeon1.mp4".

Option B (quick hack): copy just the file into ~/.openclaw/media/

mkdir -p /home/exedev/.openclaw/media
cp /home/exedev/.openclaw/workspace-relationships/media/pigeon1.mp4 /home/exedev/.openclaw/media/

Then send MEDIA:/home/exedev/.openclaw/media/pigeon1.mp4 (or message media: pointing there).

Option A is what you want long-term if this agent will routinely generate/send media.

stiff adder
hybrid shardBOT
#

Yep — two different “access” concepts here:

1) What the sandboxed tools can read/write

If you set:

relationships.workspace = /home/exedev/.openclaw/workspace/relationships

and workspaceAccess: "rw", then the container’s /workspace is that folder only. So exec/read/write/edit inside the sandbox can’t see sibling folders like /home/exedev/.openclaw/workspace/main etc.

2) What the gateway is allowed to attach as “local media”

This is separate. The host-side media allowlist (the thing throwing path-not-allowed) includes the whole:

/home/exedev/.openclaw/workspace/

tree by default.

So: if you put multiple agent workspaces under ~/.openclaw/workspace/, then in principle an agent that can call message could try to attach a file from another workspace by referencing its absolute host path (even if the sandbox FS tools can’t read it).

#

Practical guidance for your untrusted-friends setup:

  • Put only the untrusted agent’s workspace under ~/.openclaw/workspace/<id> (or keep other workspaces outside that root), or
  • split trust boundaries properly (separate OS user / separate gateway) if you truly have mutually untrusted users.
stiff adder
#

should i do the same for coder? is it the way openclaw intended?

hybrid shardBOT
#

You shouldn’t have to, no.

OpenClaw’s “intended” default layout is (still) typically ~/.openclaw/workspace-<agentId> — and the fact you’re hitting path-not-allowed for workspace-relationships is basically the allowlist hardening + the sender path not applying an explicit agent-scoped roots list in that flow (so it falls back to the default roots, which intentionally reject workspace-*).

What I’d do in your setup

  • Move only the untrusted relationships workspace under ~/.openclaw/workspace/relationships as a pragmatic workaround so media sending works.
  • Do not move coder unless you actually need coder to send local media too.

This has a nice security property for your “untrusted friends” case:

  • default allowlist includes ~/.openclaw/workspace/**
  • but it does not include ~/.openclaw/workspace-coder / ~/.openclaw/workspace-main
    So keeping trusted agents on workspace-coder / workspace-main keeps them out of that broad default root.

If you do want coder to send local media later, you can either:

  • also move it under ~/.openclaw/workspace/coder, or
  • keep its workspace as-is and copy the one file you want to send into ~/.openclaw/media/ (which is always allowed).
#

And yeah: this smells like a bug/oversight worth filing, with the log line you captured (code=path-not-allowed for workspace-relationships).

stiff adder
#

what if i change the mount name in docker

hybrid shardBOT
#

Changing the mount inside Docker won’t solve this one.

Your current failure is:

code=path-not-allowed for the host path
/home/exedev/.openclaw/workspace-relationships/media/pigeon1.mp4

That allowlist check happens on the gateway host, against host filesystem paths. Renaming /workspace inside the container (or changing the container’s mount destination) doesn’t change what the gateway is trying to read/allow on the host.

Also, OpenClaw manages that container — any manual docker run / docker update type change will get blown away the next time the sandbox is recreated.

If you want it to pass the allowlist without patching OpenClaw, you need the host path to land under an allowed root, e.g.:

  • move the agent workspace to ~/.openclaw/workspace/relationships (recommended workaround), or
  • copy the media into ~/.openclaw/media/ before sending.

If you want to keep workspace-relationships specifically, then it’s a code/config bug upstream (the send path isn’t applying agent-scoped localRoots/sandboxValidated in this flow) → that’s GitHub-issue territory.

stiff adder
#

this is a bug right?

vast larkBOT
#

@stiff adder, this thread is getting very long and answers may not be accurate due to the large context. Please start a new thread for any different problems/topics. @hybrid shard please sum up the answer to the initial message and the conversation briefly.

hybrid shardBOT
#

Yeah — I’d call this a bug / integration gap in 2026.3.2, not “expected user config”.

Reason:

  • workspace-relationships is a totally normal/default-ish workspace pattern.
  • The media loader (src/web/media.ts) intentionally blocks workspace-* only when it’s using the default localRoots (security hardening).
  • In your failing path, the send flow is clearly not applying an explicit agent-scoped localRoots (or sandboxValidated read), so it falls back to the default roots and trips the workspace-* block.

So the gateway ends up rejecting a file that:

  • exists, and
  • is in the agent workspace, and
  • should be attachable from a sandboxed agent using the message tool.

What to do

  • Use the workaround (move relationships workspace under ~/.openclaw/workspace/relationships, or copy to ~/.openclaw/media/), and
  • file a GitHub issue with:
    • OpenClaw version 2026.3.2 (85377a2)
#
  • the exact log line: code=path-not-allowed for .../workspace-relationships/...
    • repro: “sandboxed relationships agent, message tool with media: /workspace/media/pigeon1.mp4”

That’s enough for maintainers to fix the root cause (make that flow pass agent-scoped roots / sandboxValidated).

stiff adder
#

@hybrid shard can you summarise the question, what i am trying to achieve and where am i stuck? what are the probable fixes

hybrid shardBOT
#

Here’s the clean summary of what you’re trying to do, what’s working, where you’re stuck, and the likely fixes.

What you’re trying to achieve

  • Run an untrusted agent (relationships) in Docker sandbox mode so it can safely use exec (ffmpeg / media processing) without host access.
  • Still allow that agent to reply on WhatsApp/Telegram and send media attachments back (video/audio/images).
  • Avoid insecure workarounds like tools.exec.host=gateway (host exec).

What’s actually true (important correction)

  • With OpenClaw tool sandboxing, the container does not need to reach the gateway over TCP. The gateway still owns messaging; only the tools run in Docker.

Where you’re stuck (current hard blocker)

You can generate/see the file in the sandbox at:

  • /workspace/media/pigeon1.mp4 ✅

And /workspace is mounted from the host workspace:

#
  • /home/exedev/.openclaw/workspace-relationships ✅

The file also exists on the host ✅

But sending fails with:

  • LocalMediaAccessError: Local media path is not under an allowed directory ...
  • code=path-not-allowed

So this is now purely a local media allowlist issue on the gateway side (not network, not “file missing”).

Why it happens (root cause)

There’s a hardening rule in the host media loader (src/web/media.ts): when it’s using default allowed roots, it intentionally rejects paths under state dir that look like workspace-* (like ~/.openclaw/workspace-relationships/...) unless an explicit per-agent root list is applied in that send path.

In your WhatsApp send flow, that explicit agent-scoped root list isn’t being applied, so it falls back to defaults and blocks workspace-relationships.

Probable fixes (in order of practicality)

#

Fix 1 (best workaround): move the relationships workspace under ~/.openclaw/workspace/relationships

This lands in a default-allowed root and stops path-not-allowed.

Steps:

  1. move directory
  2. update agents.list.relationships.workspace
  3. openclaw gateway restart
  4. openclaw sandbox recreate --agent relationships

Fix 2 (quick hack): copy the file into ~/.openclaw/media/ before sending

~/.openclaw/media is always allowed by default. Good for a test; annoying for automation.

Fix 3 (real fix): upstream bugfix

Open a GitHub issue with the exact log line (code=path-not-allowed on workspace-relationships). The “right” behavior is that this flow should apply agent-scoped roots (or sandbox-validated reads) so agent workspaces work without hacks.

Fix 4 (security-first): split trust boundaries

If you truly have mutually untrusted users, the robust solution is separate gateway/OS user for the untrusted agent. Otherwise local-media allowlists that include broad roots can always become an exfil path.

#

If you tell me which workaround you prefer (move workspace vs copy-to-media), I can give you the exact minimal sequence of commands/config edits for that path.

stiff adder
#

Will file bug after confirming on repo, can you check existing open issues/prs related to this?

hybrid shardBOT
#

Found a few existing issues that are very close to what you’re hitting (same LocalMediaAccessError: ... code=path-not-allowed / “mediaLocalRoots not threaded through” class of bug):

Closest match (workspace-<id> specifically)

  • #30871 — “Slack: agent workspace-<id> media paths blocked after 2026.2.26 security hardening (regression)”
    https://github.com/openclaw/openclaw/issues/30871
    This is basically your symptom, just on Slack. It explicitly calls out assertLocalMediaAllowed + missing agentId/localRoots in the send pipeline.

Same root cause pattern (channel/adapters dropping mediaLocalRoots)

Related path-not-allowed family

#

https://github.com/openclaw/openclaw/issues/15541

I didn’t confirm an existing WhatsApp-specific issue yet, but your report likely belongs either as:

  • a comment/repro on #30871 (if it’s truly the same “workspace-<id> blocked because localRoots not passed”), or
  • a new issue “WhatsApp/web-outbound: message tool media from workspace-<agentId> rejected (path-not-allowed)” linking to #30871/#28802/#20029.

If you want, paste your final minimal repro steps (config + one message call + the exact error line), and I’ll suggest whether to file new vs attach to #30871.

stiff adder
#

hey @hybrid shard what's the better fix for this issue?

  • fix the WhatsApp gateway media path by propagating mediaLocalRoots through outbound send serialization/deserialization (executeSendAction -> sendMessage -> gateway send
    -> deliverOutboundPayloads).

  • add caller-side context recovery (requesterAgentIdOverride / requester session-derived agentId) so sends still get agent-scoped roots when session context is missing.

hybrid shardBOT
#

Between those two, I’d treat “missing agent context” as the primary bug, and propagating mediaLocalRoots as a secondary/compatibility fix.

Why

mediaLocalRoots is derived data (it’s just getAgentScopedMediaLocalRoots(cfg, agentId)), and the security model is better if the gateway computes it from trusted context (agentId/sessionKey), rather than trusting a roots array serialized through multiple layers.

In your failing trace, the giveaway is:

  • the path is in workspace-relationships (agent workspace)
  • but the send path is acting like it doesn’t know the agent, so it falls back to default roots → path-not-allowed

That points to agentId/sessionKey context not being available at the point where local media is validated/loaded.

“Better fix” recommendation

  1. Fix caller-side context recovery / propagation of agent identity (your option 2), so every outbound send knows:
    • agentId (or at least a sessionKey that can be parsed into agentId)
    • and (for sandboxed runs) the sandboxRoot context so /workspace/... can be normalized safely
#

Concretely: don’t rely on optional requesterAgentIdOverride; make the send pipeline always carry some agent identity from the originating session/tool invocation.

  1. Then, if there is a serialization boundary (executeSendAction -> gateway send -> deliverOutboundPayloads) where that context gets lost, serialize agentId (or sessionKey), not the computed mediaLocalRoots. Compute mediaLocalRoots on the gateway right before calling loadWebMedia.

When option 1 is still useful

If you have channel adapters/extensions that already accept mediaLocalRoots and you want a low-risk patch, threading it through can be a quick win. But long-term it’s more brittle (easy to forget in one adapter, easy to desync with future policy).

If you want, paste the structs/types for the WhatsApp outbound payload you’re passing through that chain, and I’ll point at the minimal place to add agentId/sessionKey so the gateway can compute roots consistently.

stiff adder
#

compact our conversation and then say

• I would not fully confirm that take.

What I can confirm from code:

  • Standard message/send flow already has agent context in most paths (runMessageAction resolves agentId and derives roots there).
  • The concrete break in your reported path was the gateway boundary for WhatsApp, where roots were not consistently carried end-to-end.
  • Only WhatsApp is built-in deliveryMode: "gateway" right now, so that boundary bug is the most direct explanation.

So for your issue:

  • Primary practical fix: gateway/outbound roots propagation (maintainer-style fix).
  • Secondary hardening: missing-context fallback (your fix) only if you can still reproduce after primary fix.

Long-term architecture point is fair: deriving roots from agentId/sessionKey at the gateway is cleaner than passing raw roots. But that is a design improvement, not the only valid immediate fix
here.

hybrid shardBOT
#

Compact recap (what you were trying to do / where you got stuck)

  • You’re running an untrusted relationships agent with Docker tool-sandboxing so it can safely run exec (ffmpeg/media processing) without host exec.

  • You want that agent to still be able to reply on WhatsApp/Telegram and send local media (e.g. /workspace/media/pigeon1.mp4) via the message tool / MEDIA: directives.

  • Networking to the gateway from the container is not the real problem in tool-sandboxing.

  • The hard failure you hit is:

    LocalMediaAccessError: Local media path is not under an allowed directory ... code=path-not-allowed

    even though the file exists on disk and /workspace is mounted to the expected host workspace.

  • Root issue class: local media allowlisting / roots context is not applied consistently in your WhatsApp delivery path, so it falls back to default roots and rejects workspace-<agentId> paths.


• I would not fully confirm that take.

#

What I can confirm from code:

  • Standard message/send flow already has agent context in most paths (runMessageAction resolves agentId and derives roots there).
  • The concrete break in your reported path was the gateway boundary for WhatsApp, where roots were not consistently carried end-to-end.
  • Only WhatsApp is built-in deliveryMode: "gateway" right now, so that boundary bug is the most direct explanation.

So for your issue:

  • Primary practical fix: gateway/outbound roots propagation (maintainer-style fix).
  • Secondary hardening: missing-context fallback (your fix) only if you can still reproduce after primary fix.

Long-term architecture point is fair: deriving roots from agentId/sessionKey at the gateway is cleaner than passing raw roots. But that is a design improvement, not the only valid immediate fix here.

stiff adder
#

?

hybrid shardBOT
#

If that “compact + bullets” text looks good to you, the next concrete step is just turning it into an issue comment / new issue body.

If you’re asking what to do next, I’d do this:

  1. Comment on #30871 (it’s the closest “workspace-<id> path-not-allowed due to missing roots” umbrella) with your WhatsApp-specific repro + log line.
  2. If maintainers prefer channel-specific tracking, open a new issue like:
    “WhatsApp (deliveryMode=gateway): message tool local media from workspace-<agentId> rejected (path-not-allowed)”
    and cross-link #30871 / #20029 / #28802.

Minimum payload to include:

  • OpenClaw version + commit (2026.3.2 (85377a2))
  • Agent workspace path (~/.openclaw/workspace-relationships/...)
  • Exact error (including code=path-not-allowed)
  • One-step repro: message tool send with media: "/workspace/media/pigeon1.mp4" (file exists)

If your “?” was about something else (e.g., you want me to rewrite the issue title/body, or you’re unsure whether to file new vs comment), tell me which and I’ll tighten it.

stiff adder
#

i wanted to ask out of those two fixes which one is the best one?