Bug: Stale plugin-runtime-deps directory causes Gateway crash-loop after openclaw update
Version Info
- Before: 2026.4.24
- After: 2026.4.26
- OS: macOS 26.4.1 (arm64), Node 25.9.0
- Install: npm global (
openclaw update)
Symptom
After running openclaw update, Gateway crashes and enters a launchd crash-loop for 5 hours, with no logs produced by the new process. Recovery required manually removing the old plugin-runtime-deps directory.
Root Cause (detailed data)
Timeline:
| Time (GMT+8) |
Event |
| 13:08 |
openclaw update runs, npm installs 2026.4.26 |
| 13:10:25 |
Old Gateway process (v2026.4.24) receives SIGTERM |
| 13:10:26 |
Old process reloads config, discovers bundled feishu/whatsapp plugins require >=2026.4.25 → version check fails → shutdown error |
| 13:10 ~ 18:12 |
launchd repeatedly restarts new Gateway binary → process crashes before reaching logger output → launchd throttle puts it on ice for 5 hours |
| 18:12 |
Manual gateway stop → mv old runtime-deps → gateway start → recovered |
Two-layer failure:
-
Old process version check failure: After update, the still-running v2026.4.24 Gateway reloads config, finds bundled feishu/whatsapp plugins from 2026.4.26 that require >=2026.4.25, but the running host is 2026.4.24 → fatal config error → exit.
-
New process crashes due to stale runtime-deps (the core bug): launchd starts the new v2026.4.26 binary, but plugin-runtime-deps/ now contains three directories:
openclaw-2026.4.24-da6bdffc3d96/ ← old, 4009 JS files, full core code
openclaw-2026.4.26-da6bdffc3d96/ ← new core deps, 3621 JS files
openclaw-unknown-bd1f0b6280a9/ ← new channel layer deps, 3509 JS files
- Lexicographic ordering:
2026.4.24 < 2026.4.26, so the old directory is scanned first
- The old directory has no
package.json (runtime-deps only have .openclaw-runtime-deps.json), but its dist/ contains the full OpenClaw core modules (min-host-version, version, etc.)
- 2026.4.26 introduced a layered runtime-deps architecture (previously a single flat directory), but
openclaw update doesn't clean up the old format directory
- The new Gateway binary scans the old dist/ during startup → module loading conflict → crash at Node.js initialization stage, before the logger is even available
Evidence:
gateway.err.log jumps directly from 13:10:26 to 18:12:36 — no new process logs in between
- macOS launchd shows
runs = 2 (processes that exit within 10 seconds are classified as crashes, not counted as normal runs)
- Manually removing the old directory immediately resolved the issue
- Old and new
dist/ directories have many identically-named but different JS files (e.g., min-host-version-xxx.js, version-xxx.js)
Proposed Fix
Add automatic cleanup to the openclaw update flow:
- After npm install completes, scan
plugin-runtime-deps/
- Recognize directory naming pattern
openclaw-{version}-{hash} or openclaw-unknown-{hash}
- Call
resolveBinaryVersion() to get the current installed version
- Remove directories whose version doesn't match (keep
openclaw-unknown-*)
- Log which directories were cleaned up
- Perform a hard restart (not old-process reload) to ensure the new binary starts fresh
Pseudo-code:
async function cleanupOldRuntimeDeps(currentVersion: string) {
const depsDir = path.join(configDir, 'plugin-runtime-deps');
const entries = await fs.readdir(depsDir);
for (const entry of entries) {
const match = entry.match(/^openclaw-(\d+\.\d+\.\d+)-/);
if (match && match[1] !== currentVersion) {
const fullPath = path.join(depsDir, entry);
await fs.rm(fullPath, { recursive: true });
log.info(`[update] cleaned up old runtime deps: ${entry}`);
}
}
}
Temporary Workaround
openclaw gateway stop
find ~/.openclaw/plugin-runtime-deps/ -maxdepth 1 -type d \
-name "openclaw-*" ! -name "openclaw-$(openclaw --version | grep -oE '\d+\.\d+\.\d+')-*" \
! -name "openclaw-unknown-*" -exec rm -rf {} \;
openclaw gateway start
Bug: Stale plugin-runtime-deps directory causes Gateway crash-loop after
openclaw updateVersion Info
openclaw update)Symptom
After running
openclaw update, Gateway crashes and enters a launchd crash-loop for 5 hours, with no logs produced by the new process. Recovery required manually removing the oldplugin-runtime-depsdirectory.Root Cause (detailed data)
Timeline:
openclaw updateruns, npm installs 2026.4.26gateway stop→ mv old runtime-deps →gateway start→ recoveredTwo-layer failure:
Old process version check failure: After update, the still-running v2026.4.24 Gateway reloads config, finds bundled feishu/whatsapp plugins from 2026.4.26 that require
>=2026.4.25, but the running host is 2026.4.24 → fatal config error → exit.New process crashes due to stale runtime-deps (the core bug): launchd starts the new v2026.4.26 binary, but
plugin-runtime-deps/now contains three directories:2026.4.24<2026.4.26, so the old directory is scanned firstpackage.json(runtime-deps only have.openclaw-runtime-deps.json), but itsdist/contains the full OpenClaw core modules (min-host-version,version, etc.)openclaw updatedoesn't clean up the old format directoryEvidence:
gateway.err.logjumps directly from 13:10:26 to 18:12:36 — no new process logs in betweenruns = 2(processes that exit within 10 seconds are classified as crashes, not counted as normal runs)dist/directories have many identically-named but different JS files (e.g.,min-host-version-xxx.js,version-xxx.js)Proposed Fix
Add automatic cleanup to the
openclaw updateflow:plugin-runtime-deps/openclaw-{version}-{hash}oropenclaw-unknown-{hash}resolveBinaryVersion()to get the current installed versionopenclaw-unknown-*)Pseudo-code:
Temporary Workaround