openclaw - ✅(Solved) Fix tools.exec: binder fail-closed rejects ALL absolute-path commands in v2026.4.11+v2026.4.12 (with reproducible env workaround) [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#66524Fetched 2026-04-15 06:25:50
View on GitHub
Comments
0
Participants
1
Timeline
2
Reactions
0
Timeline (top)
cross-referenced ×2

Since v2026.4.11, the tools.exec approval binder rejects every command sent from a gateway agent to a node host, including trivial absolute-path commands like /usr/bin/whoami. The error returned is:

INVALID_REQUEST: SYSTEM_RUN_DENIED: approval cannot safely bind this interpreter/runtime command

v2026.4.12 did not fix this. PRs #62401 (POSIX /bin/sh -lc unwrap) and #64050 (exec-policy CLI + node-host rejection hardening) address related-but-different code paths. The fail-closed branch in requiresStableInterpreterApprovalBindingWithShellCommand() (in src/node-host/invoke-system-run-plan.ts) still rejects the command. We have also verified that v2026.4.14-beta.1 (changelog at the time of writing) does not mention this issue.

This breaks any agent flow that uses tools.exec with host: "node", including:

  • Node host helper scripts
  • Custom autofix scripts on the node host
  • Trivial diagnostic commands (whoami, uptime, kubectl get pods)
  • YouTube transcription pipelines that invoke binaries from agents

Error Message

2026-04-13T15:27:54.866 [ws] ⇄ res ✗ node.invoke 8ms errorCode=UNAVAILABLE \
  errorMessage=INVALID_REQUEST: SYSTEM_RUN_DENIED: approval cannot safely bind \
  this interpreter/runtime command

2026-04-13T15:27:54.867 tools: exec failed stack:
GatewayClientRequestError: INVALID_REQUEST: SYSTEM_RUN_DENIED: approval cannot \
  safely bind this interpreter/runtime command
    at GatewayClient.handleMessage (file:///app/dist/client-C7CdT-9h.js:680:25)
    at WebSocket.<anonymous> (file:///app/dist/client-C7CdT-9h.js:363:35)

2026-04-13T15:27:54.875 [tools] exec failed: INVALID_REQUEST: SYSTEM_RUN_DENIED: \
  approval cannot safely bind this interpreter/runtime command \
  raw_params={"command":"/usr/bin/whoami","host":"node","node":"<node-name>","yieldMs":10000,"timeout":20}

The command /usr/bin/whoami is:

  • An absolute path
  • An explicit ELF binary (not a shell builtin)
  • Not wrapped in /bin/sh -lc (so PR #62401 does not apply)
  • Not an interpreter (sh/bash/python/node) so the "interpreter/runtime command" rejection is misleading
  • Listed in ~/.openclaw/exec-approvals.json allowlist with --agent "*"

Root Cause

Since v2026.4.11, the tools.exec approval binder rejects every command sent from a gateway agent to a node host, including trivial absolute-path commands like /usr/bin/whoami. The error returned is:

INVALID_REQUEST: SYSTEM_RUN_DENIED: approval cannot safely bind this interpreter/runtime command

v2026.4.12 did not fix this. PRs #62401 (POSIX /bin/sh -lc unwrap) and #64050 (exec-policy CLI + node-host rejection hardening) address related-but-different code paths. The fail-closed branch in requiresStableInterpreterApprovalBindingWithShellCommand() (in src/node-host/invoke-system-run-plan.ts) still rejects the command. We have also verified that v2026.4.14-beta.1 (changelog at the time of writing) does not mention this issue.

This breaks any agent flow that uses tools.exec with host: "node", including:

  • Node host helper scripts
  • Custom autofix scripts on the node host
  • Trivial diagnostic commands (whoami, uptime, kubectl get pods)
  • YouTube transcription pipelines that invoke binaries from agents

Fix Action

Fix / Workaround

  • OpenClaw version: 2026.4.12 (also reproduced on 2026.4.11)
  • Image: ghcr.io/openclaw/openclaw:2026.4.12
  • Deployment: Kubernetes (RKE2 single-node, Longhorn storage, Traefik ingress)
  • Gateway bind: loopback + socat sidecar (CWE-319 mitigation)
  • Node host: separate systemd service on host, CLI synced to 2026.4.12
  • tools.exec from pod: host=node, security=allowlist, ask=off
  • Effective policy from node host (via openclaw exec-policy show): security=full, ask=off
  • Allowlist file ~/.openclaw/exec-approvals.json: present with valid entries (including /usr/bin/whoami added explicitly during reproduction)
  • The gateway side reports node-side security=unknown, ask=unknown from exec-policy show — the gateway cannot read the effective policy from the node runtime even when the node host has it set.

Hypothesis (refined with reproducible workaround)

  1. Resolve to a file on disk (any absolute path)
  2. Have argv of the form [<absolute_binary_path>] (single element argv, no dispatch wrapper)
  3. Are not explicitly recognized as one of: MUTABLE_ARGV1_INTERPRETER_PATTERNS (node, perl, php, python, ruby), GENERIC_MUTABLE_SCRIPT_RUNNERS (esno, jiti, ts-node, tsx, vite-node), OPAQUE_MUTABLE_SCRIPT_RUNNERS (busybox, toybox), or shell wrappers (/bin/sh -lc "...")

PR fix notes

PR #66731: fix(node-host): allow absolute-path native binaries through approval binder

Description (problem / solution / changelog)

Summary

Fix the node-host approval binder so absolute-path native binaries are not rejected as unsafe interpreter/runtime commands.

Before this change, tools.exec with host=node could fail for commands like /usr/bin/whoami because the shell-payload binder treated any existing file at argv[0] as requiring stable script-style binding. That was correct for mutable scripts, but wrong for native binaries.

This patch keeps the fail-closed behavior for likely script files while allowing native executables through without requiring a mutable file operand.

Fixes #66524.

What changed

  • updated src/node-host/invoke-system-run-plan.ts
  • refined shellPayloadNeedsStableBinding() to distinguish:
    • script-like file payloads that should stay fail-closed
    • native binaries that should be allowed
  • added lightweight executable header detection for ELF, Mach-O, and PE/COFF
  • added regression tests in src/node-host/invoke-system-run-plan.test.ts for:
    • /bin/sh -lc /usr/bin/whoami succeeds
    • /bin/sh -lc <script-path> still fails closed

Why

The previous logic rejected all shell payloads whose first token resolved to an existing file, which unintentionally blocked absolute-path binaries on node hosts. The env /usr/bin/whoami workaround reported in #66524 demonstrated that the rejection was overly broad.

This change narrows the rejection surface to mutable script-like targets rather than all file-backed commands.

Risk

Low to medium.

The patch only affects the shell-payload stable-binding heuristic. It preserves the existing stricter behavior for script/interpreter cases and does not change allowlist, approval, or execution policy flows.

Testing

Added regression tests for:

  • absolute-path native binary payloads
  • script-path payloads that must remain fail-closed

Local verification:

  • git diff --check passed

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • src/node-host/invoke-system-run-plan.test.ts (modified, +194/-1)
  • src/node-host/invoke-system-run-plan.ts (modified, +95/-1)

Code Example

INVALID_REQUEST: SYSTEM_RUN_DENIED: approval cannot safely bind this interpreter/runtime command

---

# 1. Make sure the binary is in the allowlist
openclaw approvals allowlist add --agent "*" "/usr/bin/whoami"

# 2. From the pod, attempt to run the simplest possible absolute-path command via an agent
kubectl exec -n openclaw deploy/openclaw -c openclaw -- \
  openclaw agent --agent <agent_id> --message "execute /usr/bin/whoami via tools.exec on host=node"

# 3. Watch the gateway logs:
kubectl logs -n openclaw deploy/openclaw -c openclaw --since=5m | grep -A2 SYSTEM_RUN_DENIED

---

2026-04-13T15:27:54.866 [ws] ⇄ res ✗ node.invoke 8ms errorCode=UNAVAILABLE \
  errorMessage=INVALID_REQUEST: SYSTEM_RUN_DENIED: approval cannot safely bind \
  this interpreter/runtime command

2026-04-13T15:27:54.867 tools: exec failed stack:
GatewayClientRequestError: INVALID_REQUEST: SYSTEM_RUN_DENIED: approval cannot \
  safely bind this interpreter/runtime command
    at GatewayClient.handleMessage (file:///app/dist/client-C7CdT-9h.js:680:25)
    at WebSocket.<anonymous> (file:///app/dist/client-C7CdT-9h.js:363:35)

2026-04-13T15:27:54.875 [tools] exec failed: INVALID_REQUEST: SYSTEM_RUN_DENIED: \
  approval cannot safely bind this interpreter/runtime command \
  raw_params={"command":"/usr/bin/whoami","host":"node","node":"<node-name>","yieldMs":10000,"timeout":20}

---

# ❌ FAILS with SYSTEM_RUN_DENIED
tools.exec({ command: "/usr/bin/whoami", host: "node" })

# ✅ PASSES — node.invoke returns ✓ in 13ms
tools.exec({ command: "env /usr/bin/whoami", host: "node" })
RAW_BUFFERClick to expand / collapse

Summary

Since v2026.4.11, the tools.exec approval binder rejects every command sent from a gateway agent to a node host, including trivial absolute-path commands like /usr/bin/whoami. The error returned is:

INVALID_REQUEST: SYSTEM_RUN_DENIED: approval cannot safely bind this interpreter/runtime command

v2026.4.12 did not fix this. PRs #62401 (POSIX /bin/sh -lc unwrap) and #64050 (exec-policy CLI + node-host rejection hardening) address related-but-different code paths. The fail-closed branch in requiresStableInterpreterApprovalBindingWithShellCommand() (in src/node-host/invoke-system-run-plan.ts) still rejects the command. We have also verified that v2026.4.14-beta.1 (changelog at the time of writing) does not mention this issue.

This breaks any agent flow that uses tools.exec with host: "node", including:

  • Node host helper scripts
  • Custom autofix scripts on the node host
  • Trivial diagnostic commands (whoami, uptime, kubectl get pods)
  • YouTube transcription pipelines that invoke binaries from agents

Environment

  • OpenClaw version: 2026.4.12 (also reproduced on 2026.4.11)
  • Image: ghcr.io/openclaw/openclaw:2026.4.12
  • Deployment: Kubernetes (RKE2 single-node, Longhorn storage, Traefik ingress)
  • Gateway bind: loopback + socat sidecar (CWE-319 mitigation)
  • Node host: separate systemd service on host, CLI synced to 2026.4.12
  • tools.exec from pod: host=node, security=allowlist, ask=off
  • Effective policy from node host (via openclaw exec-policy show): security=full, ask=off
  • Allowlist file ~/.openclaw/exec-approvals.json: present with valid entries (including /usr/bin/whoami added explicitly during reproduction)
  • The gateway side reports node-side security=unknown, ask=unknown from exec-policy show — the gateway cannot read the effective policy from the node runtime even when the node host has it set.

Reproduction

Minimal repro on a 2-instance setup (gateway in pod, node host on the same server):

# 1. Make sure the binary is in the allowlist
openclaw approvals allowlist add --agent "*" "/usr/bin/whoami"

# 2. From the pod, attempt to run the simplest possible absolute-path command via an agent
kubectl exec -n openclaw deploy/openclaw -c openclaw -- \
  openclaw agent --agent <agent_id> --message "execute /usr/bin/whoami via tools.exec on host=node"

# 3. Watch the gateway logs:
kubectl logs -n openclaw deploy/openclaw -c openclaw --since=5m | grep -A2 SYSTEM_RUN_DENIED

Expected behaviour: whoami returns <user> (whichever user the node service runs as).

Actual behaviour: gateway returns INVALID_REQUEST: SYSTEM_RUN_DENIED: approval cannot safely bind this interpreter/runtime command after ~8ms, the agent surfaces the error verbatim to the channel.

Logs

2026-04-13T15:27:54.866 [ws] ⇄ res ✗ node.invoke 8ms errorCode=UNAVAILABLE \
  errorMessage=INVALID_REQUEST: SYSTEM_RUN_DENIED: approval cannot safely bind \
  this interpreter/runtime command

2026-04-13T15:27:54.867 tools: exec failed stack:
GatewayClientRequestError: INVALID_REQUEST: SYSTEM_RUN_DENIED: approval cannot \
  safely bind this interpreter/runtime command
    at GatewayClient.handleMessage (file:///app/dist/client-C7CdT-9h.js:680:25)
    at WebSocket.<anonymous> (file:///app/dist/client-C7CdT-9h.js:363:35)

2026-04-13T15:27:54.875 [tools] exec failed: INVALID_REQUEST: SYSTEM_RUN_DENIED: \
  approval cannot safely bind this interpreter/runtime command \
  raw_params={"command":"/usr/bin/whoami","host":"node","node":"<node-name>","yieldMs":10000,"timeout":20}

The command /usr/bin/whoami is:

  • An absolute path
  • An explicit ELF binary (not a shell builtin)
  • Not wrapped in /bin/sh -lc (so PR #62401 does not apply)
  • Not an interpreter (sh/bash/python/node) so the "interpreter/runtime command" rejection is misleading
  • Listed in ~/.openclaw/exec-approvals.json allowlist with --agent "*"

Hypothesis (refined with reproducible workaround)

The fail-closed branch in requiresStableInterpreterApprovalBindingWithShellCommand() is rejecting commands that:

  1. Resolve to a file on disk (any absolute path)
  2. Have argv of the form [<absolute_binary_path>] (single element argv, no dispatch wrapper)
  3. Are not explicitly recognized as one of: MUTABLE_ARGV1_INTERPRETER_PATTERNS (node, perl, php, python, ruby), GENERIC_MUTABLE_SCRIPT_RUNNERS (esno, jiti, ts-node, tsx, vite-node), OPAQUE_MUTABLE_SCRIPT_RUNNERS (busybox, toybox), or shell wrappers (/bin/sh -lc "...")

The error message says "interpreter/runtime command" but the failing command (whoami) is a regular ELF binary with no interpreter dispatch involved. The test file invoke-system-run-plan.test.ts does not contain a single test for argv = ["/usr/bin/<binary>"] — the test corpus assumes argv always has 2+ elements (interpreter + script, or wrapper + binary).

Reproducible workaround we found (and that proves the hypothesis)

Prefixing the command with env makes it pass:

# ❌ FAILS with SYSTEM_RUN_DENIED
tools.exec({ command: "/usr/bin/whoami", host: "node" })

# ✅ PASSES — node.invoke returns ✓ in 13ms
tools.exec({ command: "env /usr/bin/whoami", host: "node" })

env is a POSIX dispatch wrapper. When the binder sees argv = ["env", "/usr/bin/whoami"], it treats env as a generic dispatcher and does not enter the requiresStableInterpreterApprovalBindingWithShellCommand fail-closed path. The actual binary execution then proceeds via the normal allowlist matching.

This workaround works for ALL host=node commands, including absolute-path binaries (whoami, uptime, kubectl), absolute-path shell scripts, and any other command that previously failed. We verified end-to-end with multiple commands.

Why this is a bug (not "by design")

  1. The test file does not document or test the single-element-argv case, suggesting the case was not considered.
  2. The error message is misleading: whoami is not an "interpreter/runtime command" by any reasonable definition.
  3. There is no documented way to invoke an absolute-path binary directly without the env workaround — every documented example uses either npm/pnpm/yarn wrappers or shell pipelines.
  4. The binder fails BEFORE the allowlist matching, even when the binary is explicitly added to ~/.openclaw/exec-approvals.json via openclaw approvals allowlist add --agent "*" "/usr/bin/whoami".

Workaround currently in use

We migrated all critical operational scripts (autofix, healthcheck, node sync, auto-update) from tools.exec cron jobs inside the pod to systemd timers on the host directly. The systemd timers do not go through the gateway's tools.exec path, so they bypass the binder entirely. We also instructed all our agents (via their workspace TOOLS.md) to always prefix host=node exec commands with env. This restored stability, but:

  • Any new agent flow that needs to run a host command requires the env prefix awareness
  • YouTube transcription was broken until we applied the env workaround
  • Diagnostic commands from agents (whoami, uptime, kubectl get pods) were broken until the workaround
  • Heartbeat health probes from inside the agent context still need the prefix

Suggested fix

A patch to the binder so that:

  1. Non-interpreter binaries (regular ELF files, not in the explicit interpreter set {sh, bash, dash, zsh, fish, python, node, ruby, perl, php}) are bound to their absolute path as the operand, not treated as interpreter+arg.
  2. The "approval cannot safely bind" rejection only fires when the binder genuinely cannot identify a single concrete file operand (e.g., a wrapped script with dynamic interpreter resolution).
  3. The error message includes the resolved path and the expected operand shape so users can diagnose without reading source.
  4. The test file invoke-system-run-plan.test.ts adds tests for the single-element-argv case (argv = ["/usr/bin/whoami"]).

Related PRs and issues

  • PR #62401 (merged in v2026.4.12) — POSIX /bin/sh -lc unwrap. Different code path, doesn't fix our case.
  • PR #64050 (merged in v2026.4.12) — openclaw exec-policy show CLI + node-host rejection hardening. Helps diagnose but doesn't unblock the rejection.
  • PRs #65713 / #65714 / #65717 (security hardening) — broaden shell-wrapper detection. Risk: may broaden the rejection surface.
  • v2026.4.14-beta.1 changelog — no mention of this binder issue.

Severity

HIGH for any deployment using node-host tools.exec. Workaround (env prefix or systemd timers) exists but eliminates a documented and supported feature.

extent analysis

TL;DR

Prefixing commands with env allows them to pass through the binder, as seen in the reproducible workaround.

Guidance

  • The issue seems to stem from the binder's handling of single-element argv commands, treating them as interpreter/runtime commands even when they are regular ELF binaries.
  • To mitigate this, prefixing the command with env makes it pass, as env is treated as a generic dispatcher.
  • The suggested fix involves modifying the binder to correctly handle non-interpreter binaries and provide more informative error messages.
  • Testing the single-element-argv case in invoke-system-run-plan.test.ts would help ensure this issue is addressed.

Example

# Failing command
tools.exec({ command: "/usr/bin/whoami", host: "node" })

# Passing command with env prefix
tools.exec({ command: "env /usr/bin/whoami", host: "node" })

Notes

The provided workaround using env prefixing is effective but may not be a long-term solution. The root cause lies in the binder's logic, and addressing it directly would be more robust.

Recommendation

Apply the workaround by prefixing commands with env until a patch is available that corrects the binder's behavior, as this allows for continued use of tools.exec with node-host commands.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix tools.exec: binder fail-closed rejects ALL absolute-path commands in v2026.4.11+v2026.4.12 (with reproducible env workaround) [1 pull requests, 1 participants]