Skip to content

Self-restart after update.run deadlocks because restart sequence dynamic-imports swapped modules #79739

Description

@kwong975

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

  1. Run gateway under launchd with commands.restart=false.
  2. Trigger update.run (or run npm install -g openclaw while the gateway is running and a
    newer published version exists).
  3. New bundle is written to dist/ with regenerated hashes; old hash files are removed.
  4. 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.
  5. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions