Skip to content

[Bug]: sudo --preserve-env=HOME openclaw doctor --fix leaves root-owned files in ~/.npm/_cacache, breaking every subsequent non-root plugin runtime-deps install (EACCES) #71255

@aaajiao

Description

@aaajiao

Summary

On a global openclaw@2026.4.23 install, running sudo --preserve-env=HOME openclaw doctor --fix — the standard invocation for installing bundled plugin runtime deps as root while still letting doctor read user config at ~/.openclaw/ — leaves root-owned files inside the invoking user's ~/.npm/_cacache/.

From that point on, npm 7+'s ownership safety check fails every non-root npm call, including the gateway's own bundled plugin runtime-deps install at startup:

[plugins] 4 plugin(s) failed to initialize (validation: browser, memory-core, memory-wiki, telegram). Run 'openclaw plugins list' for details.
[gateway] [plugins] failed to install bundled runtime deps: Error: npm error code EACCES
npm error path /home/<user>/.npm/_cacache/tmp/<hash>
npm error Your cache folder contains root-owned files, due to a bug in
npm error previous versions of npm which has since been addressed.
npm error To permanently fix this problem, please run:
npm error   sudo chown -R <uid>:<gid> "/home/<user>/.npm"
[gateway] ready (1 plugin: active-memory; 3.1s)

Every plugin depending on @mariozechner/pi-ai / typebox / grammy / playwright-core fails validation (anthropic, google, openai, opencode, telegram, browser, memory-core, memory-wiki, ...). Only active-memory (and plugins with no runtime deps) load. Restarting the gateway does not help — the install fails again on every retry, each retry adding more root-owned entries.

Why this surfaces in 2026.4.23 specifically

Between 2026.4.22 and 2026.4.23, the bundled-plugin runtime-deps installer changed from per-plugin incremental installs to a single merged ~17-package install (same mechanism noted in #71233: npm install @anthropic-ai/sdk@... @aws-sdk/... @mariozechner/pi-ai@0.70.0 typebox@1.1.28 grammy@^1.42.0 playwright-core@1.59.1 ... aggregated across plugins).

Per-plugin installs in earlier versions often hit already-cached tarballs and skipped writes to _cacache/tmp/, so this latent ownership issue was intermittent or invisible. The merged install fetches and extracts every package in one shot and deterministically writes fresh _cacache/tmp/<hash> entries on every run, so the cache becomes root-owned on the first sudo doctor invocation and never recovers.

The same retry loop in 4.23 additionally re-triggers the full merged install on every failure, multiplying the root-owned footprint.

Reproduction

Clean Ubuntu 24.04 host, Node 24.14 (NodeSource setup_24.x), npm 11.9.0, no ~/.npm pre-existing:

sudo npm install -g openclaw@2026.4.23
rm -rf ~/.openclaw ~/.npm/_cacache
sudo --preserve-env=HOME openclaw doctor --fix
ls -ld ~/.npm/_cacache/content-v2/sha512/??/?? 2>/dev/null | head
# → entries owned by root:root
openclaw gateway restart
journalctl --user -u openclaw-gateway --since "30s ago" --no-pager | grep -E "ready|EACCES|failed to install"

Observed

[gateway] [plugins] failed to install bundled runtime deps: Error: npm error code EACCES
npm error path /home/aaajiao/.npm/_cacache/tmp/1e0d9168
...
[gateway] ready (1 plugin: active-memory; 3.1s)

Expected

[gateway] ready (N plugins: active-memory, anthropic, google, openai, opencode, telegram, browser, memory-core, memory-wiki, ...)

Workaround (host-side)

After each sudo --preserve-env=HOME openclaw doctor --fix, chown the npm cache back to the invoking user:

sudo chown -R $(id -u):$(id -g) ~/.npm

The next non-root openclaw doctor --fix / gateway startup then succeeds. This has to be repeated after every sudo doctor, so it's a band-aid, not a real fix.

Suggested upstream fix

Have the bundled-plugin runtime-deps installer pass an explicit --cache flag to its internal npm install subprocess, pointing at a dedicated cache directory that is either:

  • a root-owned persistent location (e.g. /var/cache/openclaw/npm-root, created with correct perms on first use), or
  • a per-invocation temporary cache ($(mktemp -d)/npm-cache, cleaned up on exit).

Either form isolates doctor's npm cache writes from the invoking user's ~/.npm/ and is independent of whether HOME is preserved, so sudo invocations stop polluting user state.

Relevant code surface (per #71233's reference to the bundled code): ensureBundledPluginRuntimeDeps / installBundledRuntimeDeps inside the bundled bundled-runtime-deps-*.js module, around the npm install <specs...> subprocess call.

Why sudo --preserve-env=HOME is the intended invocation, not user error

  • Bundled plugin runtime deps resolve to paths under the global npm prefix (/usr/lib/node_modules/openclaw/... on a typical sudo npm install -g install), so writing them requires root.
  • doctor reads user config from ~/.openclaw/openclaw.json; plain sudo openclaw doctor --fix with default sudo policy resets HOME=/root, causing doctor to create a separate ghost state under /root/.openclaw/ — a different failure mode with its own pitfalls.
  • So sudo --preserve-env=HOME is the only invocation that gets both root write capability and correct user config resolution. It just happens to also route npm cache writes into user-owned ~/.npm/ as root.

Environment

  • OpenClaw: 2026.4.23 (a979721)
  • Node: v24.14.0 (NodeSource setup_24.x, apt-held)
  • npm: 11.9.0
  • OS: Ubuntu 24.04.3 LTS, kernel 6.8.x, aarch64 (Linux VM on macOS host)
  • Install path: /usr/lib/node_modules/openclaw (via sudo npm install -g openclaw@2026.4.23)
  • Run mode: user-level systemd --user unit (openclaw-gateway.service), default hardening only — no ProtectHome=true / ProtectSystem=strict

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions