Skip to content

[Bug]: 4.22 plugin loader performs runtime npm install into ~/.npm/_cacache, fails under systemd ProtectHome=true #71070

@tconwellopenclaw

Description

@tconwellopenclaw

Bug Type
Regression (worked before, now fails)
Summary
On OpenClaw 2026.4.22, the bundled plugin loader attempts runtime npm install of channel plugin dependencies (@slack/bolt, @grammyjs/runner) into /home//.npm/_cacache/ during service startup. On systemd units that enforce ProtectHome=true (a standard hardening directive), this path is read-only, the install fails with EROFS, and the Slack and Telegram plugins fail to register. The gateway SIGTERM-loops until the service is downgraded.
Version 2026.4.11 — which bundled these plugin dependencies statically — works correctly in the same environment.
Scope
Confirmed affected: Slack (@slack/bolt), Telegram (@grammyjs/runner). Likely affects other bundled channels that rely on the same runtime-resolution path.
Environment

OpenClaw: 2026.4.22 (installed via sudo npm install -g openclaw@2026.4.22)
Node: 22.22.1
npm: 10.9.4
OS: Ubuntu 24.04 LTS x64
Install path: /usr/lib/node_modules/openclaw
Run mode: systemd system service (not a user-level daemon installed by openclaw onboard)
Relevant systemd hardening directives:

ProtectHome=true
ProtectSystem=strict
ReadWritePaths=/home//openclaw-workspace /var/log/openclaw
LoadCredential=OPENCLAW_GATEWAY_TOKEN:/etc/openclaw/gateway_token
LoadCredential=...additional secret refs...
Reproduction Steps

Install OpenClaw as a global npm package: sudo npm install -g openclaw@2026.4.22
Run OpenClaw as a systemd system service with ProtectHome=true set.
Configure Slack and Telegram channels using SecretRef (file source) for bot tokens.
Start the service: sudo systemctl start openclaw
Observe startup logs.

Expected Behavior
Bundled channel plugins register successfully on service start using statically bundled dependencies (as in 2026.4.11 and earlier). No runtime npm install should occur inside a running production service.
Actual Behavior
On startup (paraphrased from journal):
[plugins] registering channel: slack
[plugins] resolving @slack/bolt...
npm error code EROFS
npm error syscall mkdir
npm error path /home//.npm/_cacache
npm error errno -30
npm error EROFS: read-only file system, mkdir '/home//.npm/_cacache'
[plugins] failed to register channel: slack
[gateway] signal SIGTERM received
(Same pattern for Telegram with @grammyjs/runner.)
Gateway SIGTERM-loops — two restart cycles observed. Slack and Telegram never come online.
Workaround
Downgrade to 2026.4.11:
bashsudo npm install -g openclaw@2026.4.11
sudo systemctl restart openclaw
2026.4.11 bundles the affected dependencies statically and registers them without network or filesystem writes.
Root Cause Hypothesis
Between 4.11 and 4.22 the bundled-channel plugin loader began treating channel runtime dependencies as resolvable-at-startup rather than bundled-at-package-time. The 4.21 changelog entry —

"Plugins/doctor: repair bundled plugin runtime dependencies from doctor paths so packaged installs can recover missing channel/provider dependencies without broad core dependency installs."

— suggests this is intentional self-healing behavior, but the fallback path assumes a writable user home and a network-reachable npm registry inside a running service, neither of which holds in a hardened production deployment.
Proposed Fixes (in preference order)

Return to statically bundling channel runtime dependencies in the npm tarball, as 4.11 did. The self-healing path should be an optional openclaw doctor --fix operation invoked deliberately by an operator, not implicit startup behavior inside the gateway process.
Honor NPM_CONFIG_CACHE (or an OpenClaw-specific equivalent like OPENCLAW_PLUGIN_CACHE_DIR) so operators can direct plugin dep resolution to a systemd ReadWritePaths location (e.g. /var/cache/openclaw/npm/).
Fail fast with a clear operator-facing error when plugin deps cannot be resolved, rather than SIGTERM-looping. The current failure mode presents as a mysterious restart loop with the actual EROFS line buried in plugin-registration noise.

Why This Matters
Hardened systemd deployments using ProtectHome=true, ProtectSystem=strict, and explicit ReadWritePaths are the recommended pattern for running network-exposed services as non-root system users on Linux. This hardening pattern is incompatible with OpenClaw 2026.4.12+ (suspected range) as currently packaged. Production operators running OpenClaw behind proper sandboxing are blocked at 4.11 — which means also missing the Slack SecretRef reply-path fix (#68954) shipped in 4.21 and the session-pruning OOM prevention shipped in 4.20.
Related

4.15 SecretRef Slack reply-path regression (separate issue, fixed in 4.21 via #68954)
Pre-existing acpx embedded codex runtime probe fails with same EROFS on /home//.npm/_cacache — same root cause, benign probe warning in 4.11, now service-fatal in 4.22.

Metadata

Metadata

Assignees

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