openclaw - 💡(How to fix) Fix exec tool broken on Kubernetes deployments (v2026.5.18): ensureDir throws EPERM chmodding ~/.openclaw on CSI-mounted PVC

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…

Error Message

Since v2026.5.18, every exec call eventually fails on Kubernetes deployments because writeExecApprovalsRaw → ensureDir (introduced in this release as part of the exec approval realpath binding hardening) tries to chmod 0700 ~/.openclaw — but the directory's owner UID is root, while the OpenClaw container runs as a non-root user. chmodSync throws EPERM, and the new error-propagation path makes it fail-closed.

Root Cause

Since v2026.5.18, every exec call eventually fails on Kubernetes deployments because writeExecApprovalsRaw → ensureDir (introduced in this release as part of the exec approval realpath binding hardening) tries to chmod 0700 ~/.openclaw — but the directory's owner UID is root, while the OpenClaw container runs as a non-root user. chmodSync throws EPERM, and the new error-propagation path makes it fail-closed.

Fix Action

Fix / Workaround

Workaround

Code Example

spec:
  securityContext:
    fsGroup: 1000
    runAsGroup: 1000
    runAsNonRoot: true
    runAsUser: 1000
  containers:
  - name: openclaw
    securityContext:
      allowPrivilegeEscalation: false
      capabilities: { drop: [ALL] }
      readOnlyRootFilesystem: true
      runAsNonRoot: true

---

$ id
uid=1000(node) gid=1000(node) groups=1000(node)

$ stat -c "%n owner=%U:%G mode=%a" /home/openclaw /home/openclaw/.openclaw
/home/openclaw owner=root:root mode=755
/home/openclaw/.openclaw owner=root:node mode=2775

---

[tools] exec failed: EPERM: operation not permitted, chmod '/home/openclaw/.openclaw'

---

// src/infra/exec-approvals.ts
function ensureDir(filePath: string) {
  const dir = path.dirname(filePath);                  // /home/openclaw/.openclaw
  // ... safety checks ...
  try {
    fs.chmodSync(dir, 0o700);                          // <-- new in 5.18
  } catch (err) {
    if (process.platform !== "win32") {
      throw err;                                       // fails the exec
    }
  }
}

---

try {
  fs.chmodSync(dir, 0o700);
} catch {
  // best-effort on platforms where the runtime user doesn't own the directory
  // (e.g. CSI-mounted PVCs on Kubernetes where the volume root is root-owned
  // and fsGroup only changes group ownership)
}
RAW_BUFFERClick to expand / collapse

exec tool broken on Kubernetes deployments (v2026.5.18): ensureDir throws EPERM chmodding ~/.openclaw on CSI-mounted PVC

Summary

Since v2026.5.18, every exec call eventually fails on Kubernetes deployments because writeExecApprovalsRaw → ensureDir (introduced in this release as part of the exec approval realpath binding hardening) tries to chmod 0700 ~/.openclaw — but the directory's owner UID is root, while the OpenClaw container runs as a non-root user. chmodSync throws EPERM, and the new error-propagation path makes it fail-closed.

Reproduction

Deploy OpenClaw via the openclaw-operator Helm chart (v0.34.2) onto a Kubernetes cluster with a persistent volume backing /home/openclaw/.openclaw (any CSI driver — verified on vSphere CSI). Operator-generated pod spec sets:

spec:
  securityContext:
    fsGroup: 1000
    runAsGroup: 1000
    runAsNonRoot: true
    runAsUser: 1000
  containers:
  - name: openclaw
    securityContext:
      allowPrivilegeEscalation: false
      capabilities: { drop: [ALL] }
      readOnlyRootFilesystem: true
      runAsNonRoot: true

Result inside the container:

$ id
uid=1000(node) gid=1000(node) groups=1000(node)

$ stat -c "%n owner=%U:%G mode=%a" /home/openclaw /home/openclaw/.openclaw
/home/openclaw owner=root:root mode=755
/home/openclaw/.openclaw owner=root:node mode=2775

K8s fsGroup recursively re-groups the PVC to gid=1000 (node) but does not change the owner UID — the CSI-provisioned volume root is created as root:root and stays that way.

When the agent's exec tool runs with tools.exec.host: gateway, security: full, ask: off, the persist-approvals path fires:

[tools] exec failed: EPERM: operation not permitted, chmod '/home/openclaw/.openclaw'

Reverse-traced to:

// src/infra/exec-approvals.ts
function ensureDir(filePath: string) {
  const dir = path.dirname(filePath);                  // /home/openclaw/.openclaw
  // ... safety checks ...
  try {
    fs.chmodSync(dir, 0o700);                          // <-- new in 5.18
  } catch (err) {
    if (process.platform !== "win32") {
      throw err;                                       // fails the exec
    }
  }
}

writeExecApprovalsRaw is the only caller. It runs every time saveExecApprovals persists changes — which under security: full, ask: off happens for every exec invocation.

Impact

  • Breaks all exec-dependent agent capabilities on Kubernetes deployments after 5.18.
  • Affects every agent in our fleet (spud, crito, talos, rocky) and presumably every other K8s self-hoster.
  • The two other chmod calls in the same file (lines 434, 715, 727) are correctly best-effort (catch {} with no rethrow). Only ensureDir's chmod throws.

Suggested fix

Make the ensureDir chmod consistent with the other two in the same file — best-effort:

try {
  fs.chmodSync(dir, 0o700);
} catch {
  // best-effort on platforms where the runtime user doesn't own the directory
  // (e.g. CSI-mounted PVCs on Kubernetes where the volume root is root-owned
  // and fsGroup only changes group ownership)
}

The defense-in-depth posture is preserved: the file mode itself is set to 0o600, the directory is symlink-protected, and the approvals socket has its own token-based auth. Best-effort chmod on the parent dir aligns with the established pattern.

A complementary openclaw-operator change would be: have the operator's init container chown ${data}/.openclaw to runAsUser:runAsGroup after mkdir. That would make ensureDir's chmod succeed naturally without softening the upstream contract.

Workaround

Stay on v2026.5.7 (or any version pre-5.18) until either the upstream chmod softening lands or the operator gains an init-time chown step.

Environment

  • OpenClaw: v2026.5.18
  • OpenClaw operator: v0.34.2 (Helm chart 0.34.2)
  • Kubernetes: vSphere with Tanzu, TKG v1.31
  • Storage: vSphere CSI, StorageClass tkg-shared-policy
  • Agent CR config: tools.exec.host: gateway, security: full, ask: off

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 - 💡(How to fix) Fix exec tool broken on Kubernetes deployments (v2026.5.18): ensureDir throws EPERM chmodding ~/.openclaw on CSI-mounted PVC