Summary
When OpenClaw's auto-update runs npm install -g openclaw against an already-running gateway,
the in-process restart sequence cannot complete because it dynamic-imports modules whose hashes
were just rewritten in dist/. The process never exits, launchd's KeepAlive=true never gets a
chance to respawn, and tools fail with ERR_MODULE_NOT_FOUND for hours until an operator runs
launchctl kickstart -k manually.
Environment
- OpenClaw
2026.5.4 running, then in-place upgrade to 2026.5.7
- Installed via
npm install -g openclaw to /opt/homebrew/lib/node_modules/openclaw/
- launchd-managed (
ai.openclaw.gateway), KeepAlive=true, commands.restart=false
- Node v25.6.1, npm 11.9.0, macOS 25.4.0
Reproduction
- Run gateway under launchd with
commands.restart=false.
- Trigger
update.run (or run npm install -g openclaw while the gateway is running and a
newer published version exists).
- New bundle is written to
dist/ with regenerated hashes; old hash files are removed.
- The running process holds ESM-cached references to the old hashes for already-loaded modules.
Any new dynamic import (tools loaded on demand: exec, memory_search, etc.) tries to
resolve a removed file → ERR_MODULE_NOT_FOUND.
- The internal restart triggered by
update.run itself depends on dynamic-importing modules
that have just been swapped. Subsequent restart requests get coalesced as
[restart] request coalesced (already in-flight) indefinitely.
Observed log signature
[gateway] request handler failed: Error: Cannot find module
'/opt/homebrew/lib/node_modules/openclaw/dist/task-registry.maintenance-BYzPtA0L.js' imported
from '/opt/homebrew/lib/node_modules/openclaw/dist/status.summary--PaGSzgZ.js'
[gateway] SIGUSR1 restart ignored (not authorized; commands.restart=false or use gateway tool).
Config was last written by a newer OpenClaw (2026.5.7); current version is 2026.5.4.
[restart] request coalesced (already in-flight) reason=update.run
[tools] exec failed: Cannot find module
'/opt/homebrew/lib/node_modules/openclaw/dist/bash-tools-GSoqmeZd.js' imported from
'/opt/homebrew/lib/node_modules/openclaw/dist/pi-tools-B9lKPNVM.js'
/healthz keeps returning 200 throughout — only tool calls fail.
Root cause (proposed)
The post-update.run restart strategy assumes the running process can re-import its own restart
machinery from disk. When npm install -g is the update mechanism, that assumption is violated:
the very files needed to perform the restart are the ones being replaced.
Proposed fix
After a successful update.run, instead of attempting in-process restart, call
process.exit(0) (or a graceful equivalent) and rely on launchd / systemd / process supervisor
to respawn from the freshly-installed bundle. The supervisor already knows how to spawn the
process correctly; the running process trying to re-bootstrap itself from rewritten files is
the unrecoverable path.
For operators who run without a supervisor: still exit, but optionally spawn a fresh detached
child via node against the fully-resolved binary path before exiting. The new child reads the
new bundle from disk fresh.
Workaround
launchctl kickstart -k gui/<uid>/ai.openclaw.gateway
This is the recommended workaround until the post-update flow is hardened. Disk state is fine
after npm install -g openclaw completes — only the in-memory module graph is stale.
Detection (not a fix, but useful)
The condition is detectable by comparing the gateway PID's start time against
mtime(dist/index.js): if the bundle is newer than the process by more than the natural
restart window, the process is stale and a kickstart is needed.
Happy to PR if direction is welcome.
Summary
When OpenClaw's auto-update runs
npm install -g openclawagainst an already-running gateway,the in-process restart sequence cannot complete because it dynamic-imports modules whose hashes
were just rewritten in
dist/. The process never exits, launchd'sKeepAlive=truenever gets achance to respawn, and tools fail with
ERR_MODULE_NOT_FOUNDfor hours until an operator runslaunchctl kickstart -kmanually.Environment
2026.5.4running, then in-place upgrade to2026.5.7npm install -g openclawto/opt/homebrew/lib/node_modules/openclaw/ai.openclaw.gateway),KeepAlive=true,commands.restart=falseReproduction
commands.restart=false.update.run(or runnpm install -g openclawwhile the gateway is running and anewer published version exists).
dist/with regenerated hashes; old hash files are removed.Any new dynamic import (tools loaded on demand:
exec,memory_search, etc.) tries toresolve a removed file →
ERR_MODULE_NOT_FOUND.update.runitself depends on dynamic-importing modulesthat have just been swapped. Subsequent restart requests get coalesced as
[restart] request coalesced (already in-flight)indefinitely.Observed log signature
/healthzkeeps returning 200 throughout — only tool calls fail.Root cause (proposed)
The post-
update.runrestart strategy assumes the running process can re-import its own restartmachinery from disk. When
npm install -gis the update mechanism, that assumption is violated:the very files needed to perform the restart are the ones being replaced.
Proposed fix
After a successful
update.run, instead of attempting in-process restart, callprocess.exit(0)(or a graceful equivalent) and rely on launchd / systemd / process supervisorto respawn from the freshly-installed bundle. The supervisor already knows how to spawn the
process correctly; the running process trying to re-bootstrap itself from rewritten files is
the unrecoverable path.
For operators who run without a supervisor: still exit, but optionally
spawna fresh detachedchild via
nodeagainst the fully-resolved binary path before exiting. The new child reads thenew bundle from disk fresh.
Workaround
This is the recommended workaround until the post-update flow is hardened. Disk state is fine
after
npm install -g openclawcompletes — only the in-memory module graph is stale.Detection (not a fix, but useful)
The condition is detectable by comparing the gateway PID's start time against
mtime(dist/index.js): if the bundle is newer than the process by more than the naturalrestart window, the process is stale and a kickstart is needed.
Happy to PR if direction is welcome.