openclaw - ✅(Solved) Fix [Bug]: Plugin loader triggers redundant register() calls due to unstable runtimeSubagentMode cache key [2 pull requests, 1 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#61756Fetched 2026-04-08 02:54:55
View on GitHub
Comments
0
Participants
1
Timeline
10
Reactions
0
Author
Participants
Timeline (top)
referenced ×5cross-referenced ×2labeled ×2closed ×1

Plugin register() is called many times in a short period after gateway startup due to cache key mismatches caused by inconsistent allowGatewaySubagentBinding values across call sites in the message processing pipeline.

Root Cause

Plugin register() is called many times in a short period after gateway startup due to cache key mismatches caused by inconsistent allowGatewaySubagentBinding values across call sites in the message processing pipeline.

Fix Action

Fixed

PR fix notes

PR #61785: fix(plugins): reuse gateway-bindable active registry for default runtime requests

Description (problem / solution / changelog)

Summary

  • Problem: plugin loads started from a gateway-bindable runtime were not reused by later default runtime requests, so OpenClaw could trigger redundant plugin register() passes.
  • Why it matters: this matches openclaw/openclaw#61756, where repeated cache misses caused many extra plugin reloads during normal gateway message handling.
  • What changed: getCompatibleActivePluginRegistry() now treats an active gateway-bindable registry as compatible with a default runtime request when the rest of the cache context still matches, and src/plugins/loader.runtime-registry.test.ts adds regression coverage for that path.
  • What did NOT change (scope boundary): this does not change cache-key construction, runtime mode semantics outside this compatibility check, or any plugin activation behavior unrelated to default-versus-gateway-bindable registry reuse.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #61756
  • Related #61756
  • This PR fixes a bug or regression

Root Cause (if applicable)

  • Root cause: the active registry cache key encoded runtimeSubagentMode, so a registry created with allowGatewaySubagentBinding=true could not be reused by later default-runtime lookups even when all other compatibility inputs matched.
  • Missing detection / guardrail: there was no regression test for the gateway-bindable active registry default runtime lookup path.
  • Contributing context (if known): message-processing call sites can mix gateway-bindable startup loads with later default runtime requests.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: src/plugins/loader.runtime-registry.test.ts
  • Scenario the test should lock in: an active registry created with allowGatewaySubagentBinding: true should still be reused when a later request only asks for the default runtime and the remaining cache context matches.
  • Why this is the smallest reliable guardrail: the bug is in loader compatibility resolution, so this test hits the exact decision point without needing a full gateway repro.
  • Existing test that already covers this (if any): none.
  • If no new test is added, why not: N/A.

User-visible / Behavior Changes

  • Plugin registries created during gateway-bindable startup should now be reused by later default runtime requests instead of forcing redundant plugin reloads.

Diagram (if applicable)

Before:
[gateway-bindable startup load] -> [active registry cached under gateway-bindable mode]
[default runtime request] -> [cache miss] -> [reload + register() again]

After:
[gateway-bindable startup load] -> [active registry cached under gateway-bindable mode]
[default runtime request] -> [compatibility fallback hit] -> [reuse active registry]

Security Impact (required)

  • New permissions/capabilities? (No)
  • Secrets/tokens handling changed? (No)
  • New/changed network calls? (No)
  • Command/tool execution surface changed? (No)
  • Data access scope changed? (No)
  • If any Yes, explain risk + mitigation: N/A

Repro + Verification

Environment

  • OS: Ubuntu 24.04 for the reported issue; local verification ran in the ROOM shell environment.
  • Runtime/container: Node 20.20.2
  • Model/provider: N/A
  • Integration/channel (if any): plugin loader / gateway runtime path
  • Relevant config (redacted): plugin allowlist with allowGatewaySubagentBinding: true

Steps

  1. Create an active plugin registry using a load context with allowGatewaySubagentBinding: true.
  2. Resolve the compatible active registry with the same plugin/workspace context but without explicit runtime options.
  3. Verify that the active registry is reused instead of returning undefined and forcing a reload.

Expected

  • The default runtime request reuses the existing active registry.

Actual

  • Before this patch, the default runtime request missed the active registry because the cache key only matched the gateway-bindable variant.

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

  • Verified scenarios: node scripts/run-vitest.mjs run --config vitest.plugins.config.ts src/plugins/loader.runtime-registry.test.ts and node scripts/run-vitest.mjs run --config vitest.plugins.config.ts src/plugins/tools.optional.test.ts -t gateway-bindable both exited 0; git diff --check also passed locally.
  • Edge cases checked: the existing cache-key mismatch cases for different workspace and onlyPluginIds inputs still remain covered in the same test file.
  • What you did not verify: a full gateway/channel repro with live Telegram traffic, and full-repo green beyond the targeted checks above.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? (Yes)
  • Config/env changes? (No)
  • Migration needed? (No)
  • If yes, exact upgrade steps: N/A

Risks and Mitigations

  • Risk: treating gateway-bindable as a superset could accidentally reuse an incompatible registry if another compatibility input differs.
    • Mitigation: the fallback only returns the active registry when recomputing the same cache context with allowGatewaySubagentBinding: true matches the active cache key exactly.

Changed files

  • src/plugins/loader.runtime-registry.test.ts (modified, +22/-3)
  • src/plugins/loader.ts (modified, +24/-3)

PR #61854: Fix: exclude runtimeSubagentMode from plugin loader cache key (Resolves #61756)

Description (problem / solution / changelog)

Fixes #61756.

The plugin loader cache key included runtimeSubagentMode, which is derived from allowGatewaySubagentBinding. Different call sites in the message processing pipeline pass different values for this flag (some with allowGatewaySubagentBinding: true, some without), producing distinct cache keys and triggering redundant register() calls — 40+ in 24 seconds after startup as reported.

runtimeSubagentMode does not affect which plugins are loaded or how they are configured. It is only metadata stored alongside the active registry state (via setActivePluginRegistry). Removing it from the cache key lets all call sites share the same cached registry regardless of their binding mode, while the active state's runtimeSubagentMode is still correctly updated on each activation.

Changes:

  • Remove runtimeSubagentMode from buildCacheKey() parameters and cache key string in src/plugins/loader.ts
  • Update test: replace the old "does not reuse cached registries across gateway subagent binding modes" test case with a cache-miss test for pluginSdkResolution (which is still a valid cache key dimension)
  • Add new test verifying that registries ARE reused across gateway subagent binding modes

Testing:

  • pnpm check passes
  • pnpm build passes (no [INEFFECTIVE_DYNAMIC_IMPORT] warnings)
  • All 138 loader tests pass
  • Plugin tools optional tests pass (15/15)

AI-assisted (Claude). Fully tested. I understand the changes.

Changed files

  • src/plugins/loader.runtime-registry.test.ts (modified, +34/-1)
  • src/plugins/loader.test.ts (modified, +37/-4)
  • src/plugins/loader.ts (modified, +26/-6)

Code Example

08:38:48+00:00 info plugins {"subsystem":"plugins"} [byterover] Plugin loaded
08:38:48+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Browser integration: OpenClaw control on :18791, Playwright fallback=disabled
08:38:48+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Auto-discover: 12 existing skills loaded
08:38:48+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Plugin registered (17 tools, auto-discover)
08:38:49+00:00 info plugins {"subsystem":"plugins"} [byterover] Plugin loaded
08:38:49+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Browser integration: OpenClaw control on :18791, Playwright fallback=disabled
08:38:49+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Auto-discover: 12 existing skills loaded
08:38:49+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Plugin registered (17 tools, auto-discover)
08:38:49+00:00 info plugins {"subsystem":"plugins"} [byterover] Plugin loaded
08:38:49+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Browser integration: OpenClaw control on :18791, Playwright fallback=disabled
08:38:49+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Auto-discover: 12 existing skills loaded
08:38:49+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Plugin registered (17 tools, auto-discover)
08:38:49+00:00 info plugins {"subsystem":"plugins"} [byterover] Plugin loaded
08:38:49+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Browser integration: OpenClaw control on :18791, Playwright fallback=disabled
08:38:49+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Auto-discover: 12 existing skills loaded
08:38:49+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Plugin registered (17 tools, auto-discover)
08:38:50+00:00 info plugins {"subsystem":"plugins"} [byterover] Plugin loaded
08:38:50+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Browser integration: OpenClaw control on :18791, Playwright fallback=disabled
08:38:50+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Auto-discover: 12 existing skills loaded
08:38:50+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Plugin registered (17 tools, auto-discover)
08:38:50+00:00 info plugins {"subsystem":"plugins"} [byterover] Plugin loaded
08:38:50+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Browser integration: OpenClaw control on :18791, Playwright fallback=disabled
08:38:50+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Auto-discover: 12 existing skills loaded
08:38:50+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Plugin registered (17 tools, auto-discover)
08:38:51+00:00 info plugins {"subsystem":"plugins"} [byterover] Plugin loaded
08:38:51+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Browser integration: OpenClaw control on :18791, Playwright fallback=disabled
08:38:51+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Auto-discover: 12 existing skills loaded
08:38:51+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Plugin registered (17 tools, auto-discover)
08:38:51+00:00 info plugins {"subsystem":"plugins"} [byterover] Plugin loaded
08:38:51+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Browser integration: OpenClaw control on :18791, Playwright fallback=disabled
08:38:51+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Auto-discover: 12 existing skills loaded
08:38:51+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Plugin registered (17 tools, auto-discover)
08:38:52+00:00 info plugins {"subsystem":"plugins"} [byterover] Plugin loaded
RAW_BUFFERClick to expand / collapse

Bug type

Behavior bug (incorrect output/state without crash)

Beta release blocker

No

Summary

Plugin register() is called many times in a short period after gateway startup due to cache key mismatches caused by inconsistent allowGatewaySubagentBinding values across call sites in the message processing pipeline.

Steps to reproduce

  1. OpenClaw version: 2026.4.5
  2. I use ByteRover as Context Engine, version: v1.1.6
  3. I use Telegram
  4. Send messages on Telegram, then check Gateway log

Expected behavior

Plugin register() is called once at gateway startup. Subsequent plugin resolution calls during message processing should hit the cache and reuse the existing registry.

Actual behavior

Plugin register() is called 40+ times in 24 seconds after startup. Different call sites pass inconsistent allowGatewaySubagentBinding values, producing different cache keys and triggering full plugin reloads on each message.

OpenClaw version

2026.4.5

Operating system

Ubuntu 24.04

Install method

npm global

Model

OpenAI model

Provider / routing chain

openclaw -> local gateway -> openai

Additional provider/model setup details

No response

Logs, screenshots, and evidence

08:38:48+00:00 info plugins {"subsystem":"plugins"} [byterover] Plugin loaded
08:38:48+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Browser integration: OpenClaw control on :18791, Playwright fallback=disabled
08:38:48+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Auto-discover: 12 existing skills loaded
08:38:48+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Plugin registered (17 tools, auto-discover)
08:38:49+00:00 info plugins {"subsystem":"plugins"} [byterover] Plugin loaded
08:38:49+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Browser integration: OpenClaw control on :18791, Playwright fallback=disabled
08:38:49+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Auto-discover: 12 existing skills loaded
08:38:49+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Plugin registered (17 tools, auto-discover)
08:38:49+00:00 info plugins {"subsystem":"plugins"} [byterover] Plugin loaded
08:38:49+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Browser integration: OpenClaw control on :18791, Playwright fallback=disabled
08:38:49+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Auto-discover: 12 existing skills loaded
08:38:49+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Plugin registered (17 tools, auto-discover)
08:38:49+00:00 info plugins {"subsystem":"plugins"} [byterover] Plugin loaded
08:38:49+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Browser integration: OpenClaw control on :18791, Playwright fallback=disabled
08:38:49+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Auto-discover: 12 existing skills loaded
08:38:49+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Plugin registered (17 tools, auto-discover)
08:38:50+00:00 info plugins {"subsystem":"plugins"} [byterover] Plugin loaded
08:38:50+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Browser integration: OpenClaw control on :18791, Playwright fallback=disabled
08:38:50+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Auto-discover: 12 existing skills loaded
08:38:50+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Plugin registered (17 tools, auto-discover)
08:38:50+00:00 info plugins {"subsystem":"plugins"} [byterover] Plugin loaded
08:38:50+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Browser integration: OpenClaw control on :18791, Playwright fallback=disabled
08:38:50+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Auto-discover: 12 existing skills loaded
08:38:50+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Plugin registered (17 tools, auto-discover)
08:38:51+00:00 info plugins {"subsystem":"plugins"} [byterover] Plugin loaded
08:38:51+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Browser integration: OpenClaw control on :18791, Playwright fallback=disabled
08:38:51+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Auto-discover: 12 existing skills loaded
08:38:51+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Plugin registered (17 tools, auto-discover)
08:38:51+00:00 info plugins {"subsystem":"plugins"} [byterover] Plugin loaded
08:38:51+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Browser integration: OpenClaw control on :18791, Playwright fallback=disabled
08:38:51+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Auto-discover: 12 existing skills loaded
08:38:51+00:00 info plugins {"subsystem":"plugins"} [unbrowse] Plugin registered (17 tools, auto-discover)
08:38:52+00:00 info plugins {"subsystem":"plugins"} [byterover] Plugin loaded

Impact and severity

No response

Additional information

No response

extent analysis

TL;DR

Ensure consistent allowGatewaySubagentBinding values across all call sites in the message processing pipeline to prevent cache key mismatches and excessive plugin reloads.

Guidance

  • Review the code to identify where allowGatewaySubagentBinding is being set and ensure it is consistent across all relevant call sites.
  • Verify that the cache key generation logic is correct and not introducing any inconsistencies based on the allowGatewaySubagentBinding value.
  • Consider adding logging or debugging statements to track the allowGatewaySubagentBinding value at each call site to help identify where the inconsistency is being introduced.
  • Evaluate the plugin registration process to determine if there are any opportunities to optimize or cache the results to reduce the number of times register() is called.

Example

No specific code example can be provided without more context, but ensuring consistency in allowGatewaySubagentBinding values could involve something like:

const consistentAllowGatewaySubagentBinding = true; // or false, depending on the requirement
// Use consistentAllowGatewaySubagentBinding wherever allowGatewaySubagentBinding is needed

Notes

The provided logs indicate a pattern of repeated plugin loading and registration, which suggests that the issue is related to the cache key mismatches caused by inconsistent allowGatewaySubagentBinding values. Without more specific code or configuration details, the exact fix cannot be determined, but ensuring consistency in these values is a crucial step towards resolving the issue.

Recommendation

Apply a workaround by ensuring consistent allowGatewaySubagentBinding values across all call sites, as this directly addresses the identified cause of the problem.

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…

FAQ

Expected behavior

Plugin register() is called once at gateway startup. Subsequent plugin resolution calls during message processing should hit the cache and reuse the existing registry.

Still need to ship something?

×6

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

Back to top recommendations

TRENDING