openclaw - ✅(Solved) Fix fix: gateway startup and doctor --repair miss corrupt bundled runtime deps from interrupted staging [2 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#75309Fetched 2026-05-01 05:35:25
View on GitHub
Comments
1
Participants
2
Timeline
4
Reactions
2
Author
Timeline (top)
cross-referenced ×3commented ×1

When openclaw update times out during the post-install pnpm staging step (spawnSync ETIMEDOUT), node_modules is left in a corrupt/incomplete state. Neither the gateway startup prestage nor openclaw doctor --repair detect this, so channels continue to crash with errors like:

Cannot find package '...dotenv/index.js' imported from /home/.../.openclaw/plugin-runtime-deps/openclaw-2026.4.29-<hash>/node_modules/.pnpm/...

Root Cause

Both prestageGatewayBundledRuntimeDeps (gateway startup) and maybeRepairBundledPluginRuntimeDeps (openclaw doctor --repair) use hasDependencySentinel to detect missing packages. This function checks only whether node_modules/<pkg>/package.json exists with the correct version. When pnpm is interrupted mid-install, package.json files may already be written while internal package files are missing. The sentinel passes, the early-return fires, and no repair is triggered.

Fix Action

Fix

Add hasPreviousIncompleteInstall(installRoot) that checks for this condition. At both early-return sites, if an incomplete install is detected, treat all deps as missing to force a clean reinstall that overwrites any corrupt package state.

PR fix notes

PR #75310: fix: detect and recover from incomplete bundled runtime deps staging

Description (problem / solution / changelog)

What bug this fixes

Closes #75309

When openclaw update times out during the post-install pnpm staging step (ETIMEDOUT), node_modules is left in a corrupt/incomplete state. Both gateway startup (prestageGatewayBundledRuntimeDeps) and openclaw doctor --repair (maybeRepairBundledPluginRuntimeDeps) have an early-return at missing.length === 0 that fires even when the install is corrupt, leaving channels crashing at runtime.

Relationship to upstream refactor

The upstream refactor of isRuntimeDepSatisfied (in bundled-runtime-deps-materialization.ts) now also checks hasInstalledRuntimeDepEntryFiles — verifying that the package's main entry file exists on disk. This catches the specific dotenv case from the original bug report. However, packages that do not declare a main field return true from hasInstalledRuntimeDepEntryFiles unconditionally, so a corrupt install of such a package still passes the sentinel check and missing.length === 0 returns early without repair.

Fix

This PR adds defense-in-depth using isRuntimeDepsPlanMaterialized — the same completeness check the repair function itself uses internally. It verifies both the generated install manifest (package.json with name: "openclaw-runtime-deps-install") and that all package entry files satisfy hasSatisfiedInstallSpecPackages, so corrupt stages are caught regardless of whether a package declares a main field.

hasPreviousIncompleteInstall(installRoot, installSpecs) combines the check:

  1. node_modules exists → there was a previous install attempt
  2. !isRuntimeDepsPlanMaterialized(installRoot, installSpecs) → the install isn't actually complete

At both early-return sites: if incomplete, treat all deps as missing to force a clean reinstall.

Is this the best fix?

Yes. isRuntimeDepsPlanMaterialized is the authoritative completeness check already used by the repair functions — using it to gate the early-return is the minimally invasive fix with the broadest coverage.

Evidence

  • Confirmed root cause and fix on scottgl-aipc after v2026.4.29 upgrade with staging timeout
  • All 7 gateway startup tests pass
  • All 29 doctor repair tests pass
  • New tests added for both code paths

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • src/commands/doctor-bundled-plugin-runtime-deps.test.ts (modified, +36/-0)
  • src/commands/doctor-bundled-plugin-runtime-deps.ts (modified, +32/-12)
  • src/gateway/server-startup-plugins.test.ts (modified, +58/-0)
  • src/gateway/server-startup-plugins.ts (modified, +17/-4)
  • src/plugins/bundled-runtime-deps.ts (modified, +18/-0)

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

Cannot find package '...dotenv/index.js' imported from /home/.../.openclaw/plugin-runtime-deps/openclaw-2026.4.29-<hash>/node_modules/.pnpm/...
RAW_BUFFERClick to expand / collapse

Summary

When openclaw update times out during the post-install pnpm staging step (spawnSync ETIMEDOUT), node_modules is left in a corrupt/incomplete state. Neither the gateway startup prestage nor openclaw doctor --repair detect this, so channels continue to crash with errors like:

Cannot find package '...dotenv/index.js' imported from /home/.../.openclaw/plugin-runtime-deps/openclaw-2026.4.29-<hash>/node_modules/.pnpm/...

Root Cause

Both prestageGatewayBundledRuntimeDeps (gateway startup) and maybeRepairBundledPluginRuntimeDeps (openclaw doctor --repair) use hasDependencySentinel to detect missing packages. This function checks only whether node_modules/<pkg>/package.json exists with the correct version. When pnpm is interrupted mid-install, package.json files may already be written while internal package files are missing. The sentinel passes, the early-return fires, and no repair is triggered.

Reliable Signal

RETAINED_RUNTIME_DEPS_MANIFEST (.openclaw-runtime-deps.json) is written by repairBundledRuntimeDepsInstallRoot(Async) only after a fully successful install. An absent manifest combined with existing node_modules unambiguously signals a corrupt/interrupted install.

Fix

Add hasPreviousIncompleteInstall(installRoot) that checks for this condition. At both early-return sites, if an incomplete install is detected, treat all deps as missing to force a clean reinstall that overwrites any corrupt package state.

Steps to Reproduce

  1. Run openclaw update on a machine where pnpm staging may time out (or terminate the process during staging)
  2. Restart the gateway
  3. Observe channels crashing with Cannot find package despite openclaw doctor --repair reporting no issues

Evidence

  • Confirmed on scottgl-aipc after upgrading to v2026.4.29 (staging timed out with spawnSync ETIMEDOUT)
  • Manual workaround: wipe plugin-runtime-deps/openclaw-2026.4.29-*/ and restart triggers clean stage on startup
  • Fix confirmed working: all 7 gateway startup tests + 27 doctor repair tests pass after patch

Fix PR

scottgl9/openclaw — branch fix/incomplete-bundled-runtime-deps-staging

extent analysis

TL;DR

To fix the issue of openclaw update timing out and leaving node_modules in a corrupt state, implement the hasPreviousIncompleteInstall check to detect incomplete installs and force a clean reinstall.

Guidance

  • Implement the hasPreviousIncompleteInstall function to check for the absence of RETAINED_RUNTIME_DEPS_MANIFEST (.openclaw-runtime-deps.json) and the presence of node_modules to detect incomplete installs.
  • Modify prestageGatewayBundledRuntimeDeps and maybeRepairBundledPluginRuntimeDeps to use the hasPreviousIncompleteInstall check and force a clean reinstall if an incomplete install is detected.
  • Verify the fix by running the gateway startup tests and doctor repair tests after applying the patch.
  • Consider wiping the plugin-runtime-deps directory and restarting the gateway as a manual workaround until the fix is implemented.

Example

No code snippet is provided as the issue does not contain explicit code examples.

Notes

The fix relies on the presence of RETAINED_RUNTIME_DEPS_MANIFEST to detect complete installs, so ensure this file is correctly written after a successful install.

Recommendation

Apply the workaround by implementing the hasPreviousIncompleteInstall check and forcing a clean reinstall, as this fix has been confirmed to work in the provided tests.

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 fix: gateway startup and doctor --repair miss corrupt bundled runtime deps from interrupted staging [2 pull requests, 1 comments, 2 participants]