Skip to content

Bug: collectAlreadyStagedBundledRuntimeDepSpecs omits auto-loaded plugins, causing their deps to be pruned #75109

Description

@jared-rebel

Summary

collectAlreadyStagedBundledRuntimeDepSpecs calls collectBundledPluginRuntimeDeps({ extensionsDir, config }) with the user config. This causes auto-loaded bundled plugins that are not explicitly present in config.plugins.entries (e.g. memory-core) to be excluded from the dep scan. As a result their deps (e.g. chokidar@^5.0.0) never appear in alreadyStagedSpecs, which prevents them from being retained when other plugins run ensureBundledPluginRuntimeDeps.

Reproduction

Environment: OpenClaw 2026.4.27, Linux, packaged install (/usr/lib/node_modules/openclaw), memory-core not in config.plugins.entries (auto-loaded).

Trigger: Any plugin's ensureBundledPluginRuntimeDeps runs after memory-core's own run has written the manifest with chokidar@^5.0.0.

Observed behavior:

  1. memory-core's ensureBundledPluginRuntimeDeps writes manifest including chokidar@^5.0.0.
  2. The next plugin's ensureBundledPluginRuntimeDeps runs. collectAlreadyStagedBundledRuntimeDepSpecs scans with configmemory-core excluded → chokidar not in alreadyStagedSpecs.
  3. retainSpecIfActive("chokidar@^5.0.0") returns false (not in retainedAllowedSpecs).
  4. pruneRetainedRuntimeDepsManifestSpecs sees chokidar in previousSpecs but not in nextSpecsdeletes chokidar from node_modules.
  5. Next openclaw doctor run reports chokidar@^5.0.0 (used by memory-core) as missing and reinstalls it.
  6. Cycle repeats on every gateway restart.

Verification: scanBundledPluginRuntimeDeps({ packageRoot, config }) (no pluginIds) returns 33 deps with chokidar: undefined. Same call with pluginIds: effectivePluginIds returns 19 deps with chokidar correctly present.

Root cause

collectAlreadyStagedBundledRuntimeDepSpecs (in bundled-runtime-root-D11Fl_T4.js):

// CURRENT (buggy)
const { deps, pluginIds } = collectBundledPluginRuntimeDeps({
    extensionsDir,
    config: params.config  // excludes auto-loaded plugins not in config.plugins.entries
});

The purpose of this function is to find what's already physically installed in the install root — not what the current config activates. Passing config causes auto-loaded bundled plugins to be invisible here, breaking the retention chain for their deps.

Fix

Remove config from the collectBundledPluginRuntimeDeps call so all bundled plugins are scanned. The existing hasDependencySentinel([params.installRoot], dep) filter already ensures only physically-installed deps are returned, so scanning more candidate plugins is safe.

// FIXED
const { deps, pluginIds } = collectBundledPluginRuntimeDeps({
    extensionsDir
    // no config — scan all plugins; hasDependencySentinel filters to only installed ones
});

This was confirmed to stop the delete/reinstall cycle: with config omitted, chokidar appears in alreadyStagedSpecs, gets retained through other plugins' ensureBundledPluginRuntimeDeps runs, and is no longer pruned.

Impact

  • File watching (config reload, memory updates) silently broken between gateway restarts and the next doctor run.
  • openclaw doctor always reports chokidar@^5.0.0 (used by memory-core) as missing on installs where memory-core is auto-loaded.
  • Unnecessary npm reinstall on every doctor run.

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