Node upgrade: install node@N from a different node, never the one being replaced
Date: 2026-09-16 Lane: hermes Source: observed on Zappo (macbook-pro-3) while unblocking openclaw@2026.9.4
The trap
npm i -g node@24 replacing node@22 fails with:
> node@24.21.0 preinstall
> node installArchSpecificPackage
sh: node: command not found
npm error code 127
Cause: the node package's preinstall script needs a working node on PATH. When the global
node package being replaced IS that node, npm removes/unlinks it during the swap, so the script
cannot find node. It is a self-replacement: the runtime running the install is the one being
replaced. /usr/local/bin/node was a symlink into the very package tree npm was rewriting.
The fix
Run the install from a different node that is not the target package:
export PATH="/usr/local/opt/node/bin:$PATH" # Homebrew node, a separate install
npm i -g node@24 # writes into npm's global prefix
This machine had Homebrew node 26.3.1 at /usr/local/opt/node/bin already installed but
shadowed on PATH. npm config get prefix was /Users/dwayne/.npm-global, so node@24 landed
exactly at /Users/dwayne/.npm-global/lib/node_modules/node/bin/node, the path the OpenClaw
LaunchAgent already hardcoded. No plist edit was needed. Check the prefix before installing: if
the target path differs from what the service points at, the service will keep the old runtime.
Why the path mattered
~/Library/LaunchAgents/ai.openclaw.gateway.plist calls node by absolute path
(.../lib/node_modules/node/bin/node), and ~/.openclaw/service-env/ai.openclaw.gateway.env
prepends that same bin dir to PATH. Upgrading node anywhere else leaves the service on the old
runtime. Verify with the service's own argv, not which node.
Rules
- To upgrade the node that services run under, install from a different node install and confirm the resolved path matches the service's absolute argv.
- Check
npm config get prefixfirst; the package must land where the LaunchAgent points. - Never install the node package while the only
nodeon PATH comes from that same package. - Verify after:
node --versionat the service path, then the service's health endpoint. - LaunchAgents that reference a runtime by absolute path (node, python venvs) do not follow PATH changes. Any runtime upgrade must be checked against every such plist.
Node gate for OpenClaw
Installed OpenClaw version determines the node floor:
- openclaw 2026.8.1 requires
>=22.22.3 <23 || >=24.15.0 <25 || >=25.9.0 - openclaw 2026.9.4 requires
>=24.16.0 <25 || >=26.1.0
node@24.21.0 satisfies both. After the node upgrade, openclaw update --yes --json --timeout 900
completed and restarted its own gateway: CLI 2026.8.1 -> 2026.9.4, health {"ok":true,"status":"live"}.
Contrast with the Hermes gateway
The OpenClaw updater restarts its own service successfully. The Hermes updater does not when it
runs as a descendant of the Hermes gateway; that one leaves ~/.hermes/fleet_restart_pending and
requires hermes gateway restart from an outside shell. See
lessons/2026-09-16-gateway-restart-blocked-from-inside-gateway.md.