openclaw - ✅(Solved) Fix [Bug] bundled-runtime-deps installer reports success after partial install on ETIMEDOUT, leaving channel modules unable to resolve json5 [1 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#75296Fetched 2026-05-01 05:35:39
View on GitHub
Comments
1
Participants
2
Timeline
4
Reactions
2
Timeline (top)
cross-referenced ×2closed ×1commented ×1

After running openclaw update from 2026.4.26 → 2026.4.29, the bundled-runtime-deps installer reported a successful install in ~1s but actually installed only 36 of 50 specs into ~/.openclaw/plugin-runtime-deps/openclaw-2026.4.29-<hash>/node_modules/. The discord and telegram channels then crash-looped indefinitely with Cannot find package 'json5' because dist/frontmatter-Cc-V8aI2.js does an ESM import of json5 which never landed in the staged tree.

The proximate trigger appears to be the ETIMEDOUT from a child node spawn that surfaced earlier in the same update run.

Error Message

$ openclaw update … Completion cache update failed: Error: spawnSync /opt/homebrew/Cellar/node/25.9.0_2/bin/node ETIMEDOUT

Restarting service... Gateway did not become healthy after restart. Gateway version mismatch: expected 2026.4.29, running gateway reported unavailable.

Root Cause

After running openclaw update from 2026.4.26 → 2026.4.29, the bundled-runtime-deps installer reported a successful install in ~1s but actually installed only 36 of 50 specs into ~/.openclaw/plugin-runtime-deps/openclaw-2026.4.29-<hash>/node_modules/. The discord and telegram channels then crash-looped indefinitely with Cannot find package 'json5' because dist/frontmatter-Cc-V8aI2.js does an ESM import of json5 which never landed in the staged tree.

Fix Action

Fix / Workaround

Workaround (manual recovery)

PR fix notes

PR #75183: fix: simplify bundled runtime dependency repair

Description (problem / solution / changelog)

Summary

This PR unifies bundled plugin runtime-dependency repair around the package-level plan and lets npm/pnpm own dependency convergence once OpenClaw has decided a repair is required.

What changed:

  • Build the active bundled plugin dependency plan before startup/runtime imports, then repair the selected package-level install root once instead of per-plugin ad hoc scans.
  • Treat an existing node_modules tree without complete generated materialization as incomplete, even when package sentinels such as node_modules/<dep>/package.json exist.
  • Force the package-manager repair once the planner has marked a tree incomplete, so writing the generated install manifest cannot accidentally turn the repair into a no-op.
  • Keep post-install verification on requested packages and declared entry files, so package-manager success is not trusted until the staged tree is actually usable.
  • Accept generated manifest supersets for narrower plugin loads so a complete package-level stage is reused rather than pruned/reinstalled.
  • Preserve config/doctor/hot-reload behavior: config determines the plugin plan; doctor/config edits enter plan mode; startup only repairs when the plan or materialization requires it.

Fixes / related reports

Fixes #75309.

Supersedes the implementation approach in #75310 by keeping the same narrow idea but closing the reviewed hole where a generated manifest plus a no-main package sentinel could still look materialized.

Also hardens the recovery side of the already-addressed reports #75296 and #75304:

  • #75296: post-install verification already fails closed when npm/pnpm reports success but requested packages are missing; this PR additionally makes later doctor/startup repair recover from the leftover partial tree.
  • #75304: mirrored root dependencies and manifest-superset reuse already address the json5/prune crash loop; this PR prevents interrupted runtime-deps stages from staying stuck behind false package sentinels.

Not claimed: #73520, #74948, #74963, #75071, and #75288 are nearby runtime-deps lifecycle issues with different root causes.

Verification

Local:

  • pnpm docs:list
  • pnpm exec oxfmt --check --threads=1 src/plugins/bundled-runtime-deps-install.ts src/plugins/bundled-runtime-deps.ts src/plugins/bundled-runtime-deps-materialization.ts src/plugins/bundled-runtime-deps.test.ts src/commands/doctor-bundled-plugin-runtime-deps.test.ts
  • pnpm exec oxfmt --check --threads=1 CHANGELOG.md
  • pnpm check:changelog-attributions
  • git diff --check
  • pnpm test src/plugins/bundled-runtime-deps.test.ts src/commands/doctor-bundled-plugin-runtime-deps.test.ts src/gateway/server-startup-plugins.test.ts src/gateway/server.reload.test.ts -- --reporter=verbose

Blacksmith/Testbox:

  • tbx_01kqgsehj3tf33k2dmzy35ds7j: pnpm test src/plugins/bundled-runtime-deps.test.ts src/commands/doctor-bundled-plugin-runtime-deps.test.ts src/gateway/server-startup-plugins.test.ts src/gateway/server.reload.test.ts -- --reporter=verbose passed, 3 Vitest shards / 177 tests.

Broad pnpm check:changed was attempted on tbx_01kqgrw1phqcm20c9w06n1w4wx, but the Testbox full-sync omitted the tracked-but-gitignored pnpm-lock.yaml; the workaround polluted remote node_modules into the changed-file scan and tsgolint was later SIGKILLed. I do not count that polluted run as product signal.

Changed files

  • CHANGELOG.md (modified, +4/-0)
  • docs/cli/channels.md (modified, +3/-0)
  • docs/cli/configure.md (modified, +1/-0)
  • docs/cli/gateway.md (modified, +1/-1)
  • docs/cli/onboard.md (modified, +2/-0)
  • docs/cli/plugins.md (modified, +6/-2)
  • docs/docs.json (modified, +1/-0)
  • docs/gateway/doctor.md (modified, +1/-1)
  • docs/plugins/dependency-resolution.md (added, +214/-0)
  • docs/tools/acp-agents.md (modified, +2/-1)
  • docs/tools/plugin.md (modified, +6/-3)
  • scripts/lib/bundled-runtime-deps-install.mjs (modified, +18/-3)
  • scripts/postinstall-bundled-plugins.mjs (modified, +4/-146)
  • scripts/release-check.ts (modified, +1/-1)
  • src/channels/plugins/bundled.shape-guard.test.ts (modified, +2/-2)
  • src/channels/plugins/read-only.test.ts (modified, +14/-14)
  • src/channels/plugins/read-only.ts (modified, +3/-3)
  • src/cli/command-bootstrap.test.ts (modified, +28/-3)
  • src/cli/command-bootstrap.ts (modified, +12/-5)
  • src/cli/command-catalog.ts (modified, +28/-4)
  • src/cli/command-execution-startup.test.ts (modified, +7/-0)
  • src/cli/command-execution-startup.ts (modified, +1/-0)
  • src/cli/command-path-policy.test.ts (modified, +59/-68)
  • src/cli/command-path-policy.ts (modified, +1/-0)
  • src/cli/command-startup-policy.test.ts (modified, +2/-0)
  • src/cli/command-startup-policy.ts (modified, +23/-7)
  • src/cli/plugin-registry-loader.test.ts (modified, +11/-7)
  • src/cli/plugin-registry-loader.ts (modified, +10/-7)
  • src/cli/plugins-cli.list.test.ts (modified, +41/-3)
  • src/cli/plugins-cli.ts (modified, +8/-456)
  • src/cli/plugins-command-helpers.ts (modified, +8/-0)
  • src/cli/plugins-deps-command.test.ts (modified, +47/-32)
  • src/cli/plugins-deps-command.ts (modified, +40/-42)
  • src/cli/plugins-inspect-command.ts (added, +361/-0)
  • src/cli/plugins-list-command.ts (added, +114/-0)
  • src/cli/program/preaction.test.ts (modified, +9/-3)
  • src/commands/agents.providers.test.ts (modified, +1/-1)
  • src/commands/agents.providers.ts (modified, +2/-2)
  • src/commands/channels.list.auth-profiles.test.ts (modified, +2/-2)
  • src/commands/channels.remove.test.ts (modified, +45/-20)
  • src/commands/channels.resolve.test.ts (modified, +30/-24)
  • src/commands/channels/capabilities.ts (modified, +1/-1)
  • src/commands/channels/list.ts (modified, +1/-1)
  • src/commands/channels/remove.ts (modified, +9/-2)
  • src/commands/channels/resolve.ts (modified, +6/-1)
  • src/commands/channels/status-config-format.ts (modified, +1/-1)
  • src/commands/configure.wizard.test.ts (modified, +31/-1)
  • src/commands/configure.wizard.ts (modified, +2/-0)
  • src/commands/doctor-bundled-plugin-runtime-deps.test.ts (modified, +50/-18)
  • src/commands/doctor-bundled-plugin-runtime-deps.ts (modified, +25/-24)
  • src/commands/doctor-security.test.ts (modified, +2/-2)
  • src/commands/doctor-security.ts (modified, +1/-1)
  • src/commands/doctor/shared/channel-doctor.test.ts (modified, +1/-1)
  • src/commands/doctor/shared/channel-doctor.ts (modified, +1/-1)
  • src/commands/health.snapshot.test.ts (modified, +2/-2)
  • src/commands/health.ts (modified, +2/-2)
  • src/commands/onboard-non-interactive.gateway.test.ts (modified, +15/-0)
  • src/commands/onboard-non-interactive/local.ts (modified, +2/-0)
  • src/commands/post-config-runtime-deps.test.ts (added, +164/-0)
  • src/commands/post-config-runtime-deps.ts (added, +133/-0)
  • src/commands/status-all/channels.ts (modified, +3/-3)
  • src/commands/status-runtime-shared.test.ts (modified, +1/-1)
  • src/commands/status-runtime-shared.ts (modified, +1/-1)
  • src/commands/status.link-channel.ts (modified, +1/-1)
  • src/commands/status.scan-overview.test.ts (modified, +2/-2)
  • src/commands/status.scan-overview.ts (modified, +1/-1)
  • src/commands/status.scan.test.ts (modified, +3/-3)
  • src/gateway/config-reload-plan.ts (modified, +29/-0)
  • src/gateway/config-reload.test.ts (modified, +6/-0)
  • src/gateway/config-reload.ts (modified, +1/-0)
  • src/gateway/server-aux-handlers.test.ts (modified, +1/-0)
  • src/gateway/server-plugin-bootstrap.ts (modified, +5/-1)
  • src/gateway/server-plugins.ts (modified, +5/-1)
  • src/gateway/server-reload-handlers.ts (modified, +52/-0)
  • src/gateway/server-runtime-state.test.ts (added, +66/-0)
  • src/gateway/server-runtime-state.ts (modified, +5/-2)
  • src/gateway/server-startup-plugins.test.ts (modified, +130/-55)
  • src/gateway/server-startup-plugins.ts (modified, +145/-89)
  • src/gateway/server-startup-post-attach.test.ts (modified, +56/-0)
  • src/gateway/server-startup-post-attach.ts (modified, +26/-6)
  • src/gateway/server.impl.ts (modified, +66/-15)
  • src/gateway/server.reload.test.ts (modified, +92/-0)
  • src/gateway/server/readiness.test.ts (modified, +18/-0)
  • src/gateway/server/readiness.ts (modified, +3/-1)
  • src/infra/channel-summary.ts (modified, +1/-1)
  • src/infra/npm-install-env.ts (modified, +4/-0)
  • src/infra/safe-package-install.test.ts (modified, +10/-0)
  • src/infra/safe-package-install.ts (modified, +5/-0)
  • src/plugin-sdk/facade-loader.test.ts (modified, +1/-1)
  • src/plugins/bundled-runtime-deps-activity.ts (modified, +1/-5)
  • src/plugins/bundled-runtime-deps-install.ts (modified, +9/-47)
  • src/plugins/bundled-runtime-deps-lock.ts (modified, +7/-0)
  • src/plugins/bundled-runtime-deps-materialization.ts (modified, +79/-26)
  • src/plugins/bundled-runtime-deps-package-manager.ts (modified, +7/-1)
  • src/plugins/bundled-runtime-deps-roots.ts (modified, +66/-47)
  • src/plugins/bundled-runtime-deps-selection.ts (modified, +5/-5)
  • src/plugins/bundled-runtime-deps.test.ts (modified, +683/-69)
  • src/plugins/bundled-runtime-deps.ts (modified, +249/-80)
  • src/plugins/bundled-runtime-root.test.ts (modified, +109/-3)
  • src/plugins/bundled-runtime-root.ts (modified, +141/-24)

Code Example

$ openclaw update
Completion cache update failed: Error: spawnSync /opt/homebrew/Cellar/node/25.9.0_2/bin/node ETIMEDOUT

Restarting service...
Gateway did not become healthy after restart.
Gateway version mismatch: expected 2026.4.29, running gateway reported unavailable.

---

Installing bundled plugin runtime deps (50 specs): @agentclientprotocol/claude-agent-acp@0.31.1, @agentclientprotocol/sdk@0.21.0,, json5@^2.2.3,, web-push@^3.6.7, ws@^8.20.0, yaml@^2.8.3, zod@^4.3.6
Installed bundled plugin runtime deps in 1s: <same 50-spec list>

---

$ ls ~/.openclaw/plugin-runtime-deps/openclaw-2026.4.29-<hash>/node_modules/ | grep -E '^(json5|croner|dotenv|global-agent|jiti|jszip|markdown-it|node-llama-cpp|openai|semver|sqlite-vec|tar|tslog|web-push)$'
(no output — all 14 missing)

---

[discord] [default] channel exited: Cannot find package 'json5' imported from
  /Users/.../plugin-runtime-deps/openclaw-2026.4.29-<hash>/dist/frontmatter-Cc-V8aI2.js
Did you mean to import "json5/lib/index.js"?
[discord] [default] auto-restart attempt 6/10 in 163s

---

openclaw gateway stop
cd ~/.openclaw/plugin-runtime-deps/openclaw-2026.4.29-<hash>/
# Write a package.json with all 50 declared deps (so they survive subsequent re-stage)
npm install --ignore-scripts --no-audit --no-fund --no-package-lock --legacy-peer-deps
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.openclaw.gateway.plist
RAW_BUFFERClick to expand / collapse

Summary

After running openclaw update from 2026.4.26 → 2026.4.29, the bundled-runtime-deps installer reported a successful install in ~1s but actually installed only 36 of 50 specs into ~/.openclaw/plugin-runtime-deps/openclaw-2026.4.29-<hash>/node_modules/. The discord and telegram channels then crash-looped indefinitely with Cannot find package 'json5' because dist/frontmatter-Cc-V8aI2.js does an ESM import of json5 which never landed in the staged tree.

The proximate trigger appears to be the ETIMEDOUT from a child node spawn that surfaced earlier in the same update run.

Environment

  • OpenClaw: 2026.4.26 → 2026.4.29 (npm global, openclaw update)
  • macOS: Darwin 25.2.0 (arm64)
  • Node: 25.9.0_2 (homebrew)
  • Install root: ~/.openclaw/plugin-runtime-deps/openclaw-2026.4.29-<hash>/

Timeline / Evidence

$ openclaw update
Completion cache update failed: Error: spawnSync /opt/homebrew/Cellar/node/25.9.0_2/bin/node ETIMEDOUT

Restarting service...
Gateway did not become healthy after restart.
Gateway version mismatch: expected 2026.4.29, running gateway reported unavailable.

Then openclaw doctor --fix:

Installing bundled plugin runtime deps (50 specs): @agentclientprotocol/[email protected], @agentclientprotocol/[email protected], …, json5@^2.2.3, …, web-push@^3.6.7, ws@^8.20.0, yaml@^2.8.3, zod@^4.3.6
Installed bundled plugin runtime deps in 1s: <same 50-spec list>

Doctor reports success in 1 second — far too fast to install 50 specs from the registry. After this, the staged tree was missing 14 packages:

$ ls ~/.openclaw/plugin-runtime-deps/openclaw-2026.4.29-<hash>/node_modules/ | grep -E '^(json5|croner|dotenv|global-agent|jiti|jszip|markdown-it|node-llama-cpp|openai|semver|sqlite-vec|tar|tslog|web-push)$'
(no output — all 14 missing)

The staged package.json had only 41 of the 50 declared deps, and node_modules had only those 41 + transitives, no extras.

Symptom (the user-visible failure)

Gateway is "running" and reports healthy, but discord and telegram providers crash-loop with exponential backoff:

[discord] [default] channel exited: Cannot find package 'json5' imported from
  /Users/.../plugin-runtime-deps/openclaw-2026.4.29-<hash>/dist/frontmatter-Cc-V8aI2.js
Did you mean to import "json5/lib/index.js"?
[discord] [default] auto-restart attempt 6/10 in 163s

DM agent is unreachable. Doctor doesn't surface the missing packages (it lists what it thinks it installed, not what's actually present).

Root Cause (best guess)

Two compounding issues:

  1. Silent failure on partial install. The npm install <50 specs> --no-save --no-package-lock --ignore-scripts … invocation in bundled-runtime-deps-Dj2QXhNg.js exits with status 0 even when it didn't actually install all the specs (likely because some specs failed to resolve / fetch within the time budget after the upstream ETIMEDOUT, but npm doesn't propagate that as a non-zero exit when other specs succeeded). The wrapper logs installed in <duration> based on exit status, never re-verifying that each spec is present in node_modules/.

  2. No post-install verification. After npm install returns, nothing checks fs.existsSync(node_modules/<name>) for each requested spec. If the install was partial, it goes undetected until a runtime import fails.

A 1-second / 2ms / 3ms "installed N specs" log line is a strong tell that npm short-circuited — but it's reported as success.

Workaround (manual recovery)

openclaw gateway stop
cd ~/.openclaw/plugin-runtime-deps/openclaw-2026.4.29-<hash>/
# Write a package.json with all 50 declared deps (so they survive subsequent re-stage)
npm install --ignore-scripts --no-audit --no-fund --no-package-lock --legacy-peer-deps
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.openclaw.gateway.plist

After this, channels start cleanly.

Suggested Fix

In bundled-runtime-deps-Dj2QXhNg.js (the runBundledRuntimeDependencyNpmInstall path):

  1. Verify each requested spec resolves after install. After npm install returns 0, walk the requested specs and confirm require.resolve(name, { paths: [installRoot] }) (or fs.existsSync(installRoot/node_modules/name/package.json)) for each. If any are missing, surface a clear error rather than logging "installed in Nms".
  2. Treat suspiciously-fast installs as suspect. If wall time < ~50ms × spec count and the staged tree was empty before, log a warning and re-verify.
  3. Consider letting doctor --fix re-verify the existing staged tree rather than relying on npm's cache-hit short-circuit. Right now doctor --fix runs the same install and gets the same fast no-op success.
  4. Bonus: when openclaw update sees Completion cache update failed: … ETIMEDOUT, surface that as a non-fatal warning that recommends openclaw doctor --fix with verification, not just a re-run.

Related

  • #73520 — different failure mode (stale old version dirs causing gateway crash-loop), but same general area of plugin-runtime-deps lifecycle.
  • #74948 — legacy lock files; also runtime-deps lifecycle.
  • #74963 — multi-instance hash mismatch ENOENT on dist files; same staging dir but different cause.

extent analysis

TL;DR

Verify each requested spec after install and treat suspiciously-fast installs as suspect to prevent partial installations.

Guidance

  • Verify each requested spec resolves after install using require.resolve(name, { paths: [installRoot] }) or fs.existsSync(installRoot/node_modules/name/package.json) to ensure all dependencies are installed.
  • Treat suspiciously-fast installs as suspect by logging a warning and re-verifying if the wall time is less than ~50ms × spec count and the staged tree was empty before.
  • Consider re-verifying the existing staged tree in doctor --fix instead of relying on npm's cache-hit short-circuit.
  • Surface ETIMEDOUT errors as non-fatal warnings that recommend openclaw doctor --fix with verification.

Example

# Manual recovery
openclaw gateway stop
cd ~/.openclaw/plugin-runtime-deps/openclaw-2026.4.29-<hash>/
npm install --ignore-scripts --no-audit --no-fund --no-package-lock --legacy-peer-deps
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.openclaw.gateway.plist

Notes

The provided workaround can be used as a temporary solution until the suggested fixes are implemented. The bundled-runtime-deps-Dj2QXhNg.js file needs to be modified to include the suggested verification and warning logic.

Recommendation

Apply the suggested fixes to bundled-runtime-deps-Dj2QXhNg.js to prevent partial installations and ensure all dependencies are installed correctly. This will help prevent crash-loops and ensure the gateway runs smoothly.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix [Bug] bundled-runtime-deps installer reports success after partial install on ETIMEDOUT, leaving channel modules unable to resolve json5 [1 pull requests, 1 comments, 2 participants]