openclaw - ✅(Solved) Fix [P0] scoped/unscoped cache-key mismatch causes full plugin reload on every embedded message (20-40s per turn) [1 pull requests, 3 comments, 4 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#75520Fetched 2026-05-02 05:33:32
View on GitHub
Comments
3
Participants
4
Timeline
22
Reactions
2
Author
Timeline (top)
cross-referenced ×13commented ×3referenced ×3closed ×1

core-plugin-tools takes 20-32s on every embedded message run because the plugin registry is reloaded from scratch — including re-staging bundled runtime deps (e.g. acpx with 31 npm specs). Total prep stages reach 40-70s per message.

Severity: P0 — completely blocks multi-plugin deployments. Any gateway running 2+ bundled plugins is affected. Every agent turn incurs the full reload penalty, making the assistant effectively unusable for sustained conversations.

Root Cause

The resolvePluginToolRegistry() function in src/plugins/tools.ts only prefers the already-active plugin registry for gateway-bindable subagent scenarios. For regular embedded messages, it falls through to resolveRuntimePluginRegistry(), which compares cache keys.

The gateway loads plugins at startup with onlyPluginIds: startupPluginIds (scoped), but the embedded runner requests tools without scope. The cache key includes serializePluginIdScope():

  • Gateway: JSON.stringify(["acpx", "telegram", ...])
  • Embedded runner: "__unscoped__"

The keys mismatch → getCompatibleActivePluginRegistry() returns undefined → loadOpenClawPlugins() runs fresh, triggering bundled runtime dependency staging for every bundled plugin on every message.

Fix Action

Fixed

PR fix notes

PR #75521: fix(plugins): reuse active plugin registry for tool resolution on every run

Description (problem / solution / changelog)

Summary

Fixes a performance regression where core-plugin-tools takes 20-32s on every embedded message run because the plugin registry is reloaded from scratch — including re-staging bundled runtime deps (e.g. acpx with 31 npm specs). Total prep stages reach 40-70s per message.

Root Cause

The resolvePluginToolRegistry() function in src/plugins/tools.ts only preferred the already-active plugin registry for gateway-bindable subagent scenarios. For regular embedded messages, it fell through to resolveRuntimePluginRegistry(), which compares cache keys.

The gateway loads plugins at startup with onlyPluginIds: startupPluginIds (scoped), but the embedded runner requests tools without scope. The cache key includes serializePluginIdScope():

  • Gateway: JSON.stringify([\"acpx\", \"telegram\", ...])
  • Embedded runner: "__unscoped__"

The keys mismatch → getCompatibleActivePluginRegistry() returns undefined → loadOpenClawPlugins() runs fresh, triggering bundled runtime dependency staging for every bundled plugin on every message.

Fix

Move the compatibility relaxation into getCompatibleActivePluginRegistry() in src/plugins/loader.ts, preserving the full loader compatibility contract (workspace, config, activation metadata, runtime mode). When the active registry was loaded with a specific plugin scope and the current request is unscoped, we build a cache key using the active registry's successfully-loaded plugin IDs as the scope. If that scoped key matches the active cache key, the registry is compatible and is reused.

This keeps all existing compatibility checks intact while safely allowing the scoped gateway-startup registry to satisfy unscoped embedded-tool requests.

Changes

  • src/plugins/loader.ts: +38 lines in getCompatibleActivePluginRegistry() — add scoped-active → unscoped-request reuse path
  • src/plugins/tools.ts: reverted — restore original behavior
  • CHANGELOG.md: add entry for the fix

Verification

  • pnpm test src/plugins/tools.optional.test.ts src/plugins/loader.runtime-registry.test.ts — 27/27 pass
  • pnpm lint:core — clean

Closes #75520

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • src/plugins/loader.runtime-registry.test.ts (modified, +184/-0)
  • src/plugins/loader.ts (modified, +23/-4)
  • src/plugins/registry-empty.ts (modified, +1/-0)
  • src/plugins/registry-types.ts (modified, +1/-0)
  • src/plugins/registry.ts (modified, +8/-4)
  • src/plugins/tools.optional.test.ts (modified, +40/-3)
  • src/plugins/tools.ts (modified, +32/-20)

Code Example

[trace:embedded-run] prep stages: totalMs=40499
  core-plugin-tools:    24296ms
  stream-setup:         10710ms
  system-prompt:         9213ms

---

[plugins] acpx staging bundled runtime deps (31 specs)...
[plugins] memory-core staging bundled runtime deps (31 specs)...
RAW_BUFFERClick to expand / collapse

Summary

core-plugin-tools takes 20-32s on every embedded message run because the plugin registry is reloaded from scratch — including re-staging bundled runtime deps (e.g. acpx with 31 npm specs). Total prep stages reach 40-70s per message.

Severity: P0 — completely blocks multi-plugin deployments. Any gateway running 2+ bundled plugins is affected. Every agent turn incurs the full reload penalty, making the assistant effectively unusable for sustained conversations.

Symptom

[trace:embedded-run] prep stages: totalMs=40499
  core-plugin-tools:    24296ms
  stream-setup:         10710ms
  system-prompt:         9213ms

Logs show on every embedded run:

[plugins] acpx staging bundled runtime deps (31 specs)...
[plugins] memory-core staging bundled runtime deps (31 specs)...

Root Cause

The resolvePluginToolRegistry() function in src/plugins/tools.ts only prefers the already-active plugin registry for gateway-bindable subagent scenarios. For regular embedded messages, it falls through to resolveRuntimePluginRegistry(), which compares cache keys.

The gateway loads plugins at startup with onlyPluginIds: startupPluginIds (scoped), but the embedded runner requests tools without scope. The cache key includes serializePluginIdScope():

  • Gateway: JSON.stringify(["acpx", "telegram", ...])
  • Embedded runner: "__unscoped__"

The keys mismatch → getCompatibleActivePluginRegistry() returns undefined → loadOpenClawPlugins() runs fresh, triggering bundled runtime dependency staging for every bundled plugin on every message.

Fix (PR #75521)

Add a narrower reuse path in getCompatibleActivePluginRegistry() (src/plugins/loader.ts) that preserves the full loader compatibility contract (workspace, config, activation metadata, runtime mode). When the active registry was loaded with a specific plugin scope and the current request is unscoped, we build a cache key using the active registry's successfully-loaded plugin IDs as the scope. If that scoped key matches the active cache key, the registry is compatible and is reused.

This keeps all existing compatibility checks intact while safely allowing the scoped gateway-startup registry to satisfy unscoped embedded-tool requests.

Scope of Impact

  • Affected: All multi-plugin OpenClaw gateways (2+ bundled plugins enabled)
  • Not affected: Single-plugin deployments, gateway-bindable subagent scenarios (already had the fast path)
  • Regression window: v2026.4.29+

Environment

  • Version: 2026.4.29+
  • Regression introduced: Plugins/runtime-deps refactoring

extent analysis

TL;DR

Apply the fix from PR #75521 to reuse the active plugin registry for unscoped embedded-tool requests, reducing the reload penalty.

Guidance

  • Review the getCompatibleActivePluginRegistry() function in src/plugins/loader.ts to understand the current cache key comparison logic.
  • Verify that the proposed fix in PR #75521 preserves the full loader compatibility contract and safely reuses the scoped gateway-startup registry for unscoped embedded-tool requests.
  • Test the fix with multi-plugin deployments to ensure the reload penalty is reduced and the assistant becomes usable for sustained conversations.
  • Consider backporting the fix to affected versions (2026.4.29+) to minimize the regression window.

Example

No code snippet is provided as the issue already includes a clear description of the proposed fix in PR #75521.

Notes

The fix may not be applicable to single-plugin deployments or gateway-bindable subagent scenarios, which are already unaffected by the issue.

Recommendation

Apply the workaround by implementing the fix from PR #75521, as it directly addresses the root cause of the issue and has been proposed as a solution.

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 [P0] scoped/unscoped cache-key mismatch causes full plugin reload on every embedded message (20-40s per turn) [1 pull requests, 3 comments, 4 participants]