openclaw - 💡(How to fix) Fix Plugin registry cache miss causes 26+ second models.list for custom provider users (v4.29) [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#75863Fetched 2026-05-02 05:28:46
View on GitHub
Comments
1
Participants
2
Timeline
2
Reactions
2
Timeline (top)
closed ×1commented ×1

Plugin registry cache never hits for users with custom providers, causing models.list to take 26+ seconds and frequent liveness warnings. Auth stage also takes 9-13 seconds on gateway startup.

Root Cause

Two separate bugs compound to cause the performance regression:

Code Example

return resolvePluginProviders({
    ...params,
    mode: "setup",
    cache: false,          // <-- bypasses cache entirely
    ...
});

---

// Current
function resolveRuntimePluginRegistry(options) {
    if (!options || !hasExplicitCompatibilityInputs(options)) return getCompatibleActivePluginRegistry();
    const compatible = getCompatibleActivePluginRegistry(options);
    if (compatible) return compatible;
    if (isPluginRegistryLoadInFlight(options)) return;
    return loadOpenClawPlugins(options);  // slow path every time
}

// Proposed
function resolveRuntimePluginRegistry(options) {
    if (!options || !hasExplicitCompatibilityInputs(options)) return getCompatibleActivePluginRegistry();
    const compatible = getCompatibleActivePluginRegistry(options);
    if (compatible) return compatible;
    const activeRegistry = getActivePluginRegistry();
    if (activeRegistry) return activeRegistry;  // reuse already-loaded registry
    if (isPluginRegistryLoadInFlight(options)) return;
    return loadOpenClawPlugins(options);
}
RAW_BUFFERClick to expand / collapse

Summary

Plugin registry cache never hits for users with custom providers, causing models.list to take 26+ seconds and frequent liveness warnings. Auth stage also takes 9-13 seconds on gateway startup.

Environment

  • OpenClaw 2026.4.29 (a448042)
  • Custom providers configured in openclaw.json (e.g. xfyun, deepseek, siliconflow)

Symptoms

MetricExpectedActual
Auth stage (gateway startup)<1s9-13s
models.list<1s26+ seconds
Liveness warningsNoneFrequent (every 5 min, eventLoopDelayP99Ms 8000+ms)

Root Cause Analysis

Two separate bugs compound to cause the performance regression:

Bug 1: cache: false hardcoded in resolveProviderPluginsForHooks

File: provider-runtime-*.js

resolveProviderPluginsForHooks() hardcodes cache: false when calling resolvePluginProviders():

return resolvePluginProviders({
    ...params,
    mode: "setup",
    cache: false,          // <-- bypasses cache entirely
    ...
});

This causes resolveRuntimePluginRegistry() to skip its cache check (const cacheEnabled = options.cache !== false), triggering a full loadOpenClawPlugins() on every call — filesystem scan, manifest parsing, dependency install.

Fix: Change cache: falsecache: true.

Bug 2: Cache key mismatch in getCompatibleActivePluginRegistry

File: loader-*.js

resolveRuntimePluginRegistry() calls getCompatibleActivePluginRegistry(options) which does strict cache key equality comparison. The cache key is built by buildCacheKey() and includes the full plugins config (allow list, entries with API keys, etc).

When resolveProviderPluginsForHooks is called for a custom provider, the requesting context produces a different plugins description than what the active registry was loaded with:

  1. allow list mismatch: The request includes all registered provider IDs (anthropic, byteplus, google, moonshot, tencent, volcengine, xai, etc.), while the active registry only has installed plugin IDs (qqbot, discord, tokenjuice)
  2. entries config mismatch: The request has empty plugin entry configs {}, while the active registry has full configs (e.g. {"webSearch":{"apiKey":"tvly-dev-..."}})

Since the cache keys differ, getCompatibleActivePluginRegistry() returns undefined, and resolveRuntimePluginRegistry() falls through to loadOpenClawPlugins() — the slow path — every time.

Why this only affects users with custom providers:

Users without custom providers have consistent allow lists between startup and runtime requests, so cache keys match. Users with custom providers (xfyun, deepseek, etc.) trigger resolveProviderPluginsForHooks with a broader allow list that differs from the active registry's scope, causing permanent cache misses.

The design issue: getCompatibleActivePluginRegistry asks "is this cached result safe to reuse?" but implements it as "does the cache key match exactly?" — these are different questions. A subset compatibility check (requested plugins ⊆ active registry plugins, requested config doesn't conflict) would be more appropriate than strict key equality. The current implementation is overly conservative: even when the active registry contains all needed plugins, it refuses to reuse them because the requesting context has a broader allow list or different entry configs.

Proposed Fixes

Fix 1 (simple): cache: falsecache: true

In resolveProviderPluginsForHooks, change both occurrences of cache: false to cache: true.

Fix 2 (minimal): Active registry fallback

When getCompatibleActivePluginRegistry(options) returns undefined (cache key mismatch), return the already-loaded active registry instead of triggering a full reload:

// Current
function resolveRuntimePluginRegistry(options) {
    if (!options || !hasExplicitCompatibilityInputs(options)) return getCompatibleActivePluginRegistry();
    const compatible = getCompatibleActivePluginRegistry(options);
    if (compatible) return compatible;
    if (isPluginRegistryLoadInFlight(options)) return;
    return loadOpenClawPlugins(options);  // slow path every time
}

// Proposed
function resolveRuntimePluginRegistry(options) {
    if (!options || !hasExplicitCompatibilityInputs(options)) return getCompatibleActivePluginRegistry();
    const compatible = getCompatibleActivePluginRegistry(options);
    if (compatible) return compatible;
    const activeRegistry = getActivePluginRegistry();
    if (activeRegistry) return activeRegistry;  // reuse already-loaded registry
    if (isPluginRegistryLoadInFlight(options)) return;
    return loadOpenClawPlugins(options);
}

Fix 2 (proper): Subset compatibility check in getCompatibleActivePluginRegistry

Instead of strict cache key equality, check whether the requested plugin scope is a subset of the active registry's scope and whether config differences are non-conflicting. This would correctly handle the case where a broader allow list or different entry configs don't affect the validity of the cached result.

Results After Applying Fixes 1 + 2 (minimal)

MetricBeforeAfter
Auth stage9-13s0.85s
models.list26+ seconds~1.7s
Liveness warningsFrequentNone

Reproduction

  1. Install OpenClaw 4.29
  2. Add custom providers in openclaw.json (e.g. xfyun, deepseek with auth: "api-key" and apiKey fields)
  3. Start gateway: openclaw gateway start
  4. Run openclaw models list — observe 26+ second delay
  5. Check gateway logs for repeated loadOpenClawPlugins calls and liveness warnings

extent analysis

TL;DR

Change cache: false to cache: true in resolveProviderPluginsForHooks and implement a subset compatibility check in getCompatibleActivePluginRegistry to fix the plugin registry cache issue.

Guidance

  • Identify the provider-runtime-*.js file and update the resolveProviderPluginsForHooks function to set cache: true when calling resolvePluginProviders.
  • In the loader-*.js file, modify the getCompatibleActivePluginRegistry function to perform a subset compatibility check instead of strict cache key equality.
  • As a temporary workaround, consider implementing the proposed "minimal" fix in resolveRuntimePluginRegistry to reuse the already-loaded active registry when the cache key mismatch occurs.
  • Verify the fixes by checking the metrics (auth stage, models.list, and liveness warnings) after applying the changes.

Example

// Proposed change in resolveRuntimePluginRegistry
function resolveRuntimePluginRegistry(options) {
    // ...
    const compatible = getCompatibleActivePluginRegistry(options);
    if (compatible) return compatible;
    const activeRegistry = getActivePluginRegistry();
    if (activeRegistry) return activeRegistry;  // reuse already-loaded registry
    // ...
}

Notes

The provided fixes assume that the issue is caused by the hardcoded cache: false and the strict cache key equality check in getCompatibleActivePluginRegistry. However, the root cause may be more complex, and additional debugging may be necessary to fully resolve the issue.

Recommendation

Apply the proposed fixes (change cache: false to cache: true and implement a subset compatibility check) to resolve the plugin registry cache issue, as they address the identified root causes and have shown significant performance improvements in the provided results.

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 - 💡(How to fix) Fix Plugin registry cache miss causes 26+ second models.list for custom provider users (v4.29) [1 comments, 2 participants]