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
Summary
On a global
openclaw@2026.4.23install, runningsudo --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:
Every plugin depending on
@mariozechner/pi-ai/typebox/grammy/playwright-corefails validation (anthropic, google, openai, opencode, telegram, browser, memory-core, memory-wiki, ...). Onlyactive-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~/.npmpre-existing:Observed
Expected
Workaround (host-side)
After each
sudo --preserve-env=HOME openclaw doctor --fix, chown the npm cache back to the invoking user: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
--cacheflag to its internalnpm installsubprocess, pointing at a dedicated cache directory that is either:/var/cache/openclaw/npm-root, created with correct perms on first use), or$(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 whetherHOMEis preserved, so sudo invocations stop polluting user state.Relevant code surface (per #71233's reference to the bundled code):
ensureBundledPluginRuntimeDeps/installBundledRuntimeDepsinside the bundledbundled-runtime-deps-*.jsmodule, around thenpm install <specs...>subprocess call.Why
sudo --preserve-env=HOMEis the intended invocation, not user error/usr/lib/node_modules/openclaw/...on a typicalsudo npm install -ginstall), so writing them requires root.~/.openclaw/openclaw.json; plainsudo openclaw doctor --fixwith default sudo policy resetsHOME=/root, causing doctor to create a separate ghost state under/root/.openclaw/— a different failure mode with its own pitfalls.sudo --preserve-env=HOMEis 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
2026.4.23(a979721)v24.14.0(NodeSourcesetup_24.x, apt-held)11.9.0/usr/lib/node_modules/openclaw(viasudo npm install -g openclaw@2026.4.23)systemd --userunit (openclaw-gateway.service), default hardening only — noProtectHome=true/ProtectSystem=strictRelated
ProtectHome=truesandboxing (different root cause: filesystem-level block, not ownership check; but same downstream symptom of runtime-deps install failure).