openclaw - ✅(Solved) Fix openclaw doctor --non-interactive emits two false positives when memory is intentionally disabled: plugins.allow is empty warning and memory-core disabled in config error [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#68088Fetched 2026-04-18 05:54:01
View on GitHub
Comments
1
Participants
2
Timeline
5
Reactions
0
Timeline (top)
cross-referenced ×2referenced ×2commented ×1

Error Message

Error: Bundled plugin public surface access blocked for "memory-core" via memory-core/runtime-api.js: disabled in config

Root Cause

src/agents/memory-search.ts calls getMemoryEmbeddingProvider(...) at three sites without threading the real cfg through. The capability-resolution chain that follows then synthesizes a compat config that drops plugins.allow and plugins.entries, which produces both observed symptoms.

Doctor reaches memory-search config resolution through:

  • noteMemorySearchHealth(...)
  • resolveMemorySearchConfig(...)
  • mergeConfig(...)

Within that path, getMemoryEmbeddingProvider(...) was called at these three sites without passing cfg:

  1. primary provider lookup in mergeConfig(...)
  2. fallback provider lookup in mergeConfig(...)
  3. multimodal provider lookup in resolveMemorySearchConfig(...)

That call chain descends through:

  • getMemoryEmbeddingProvider
  • listMemoryEmbeddingProviders
  • resolvePluginCapabilityProviders
  • resolveRuntimePluginRegistry

When cfg is omitted, capability resolution falls onto a compat-config path. That compat config drops both:

  • plugins.allow
  • plugins.entries

As a result:

  • the loader sees an empty allowlist and emits the plugins.allow is empty warning
  • the memory-core activation guard is consulted from a config view that never had the user’s real plugins.entries.memory-core.enabled setting to begin with, producing the misleading disabled in config error

Fix Action

Fixed

PR fix notes

PR #68110: fix(memory-search): thread cfg through getMemoryEmbeddingProvider so doctor stops emitting plugins.allow and memory-core false positives

Description (problem / solution / changelog)

Summary

  • Problem: When memory is intentionally disabled (plugins.entries.memory-core.enabled: false, plugins.slots.memory: "none"), openclaw doctor --non-interactive emits two false-positive lines:
    • [plugins] plugins.allow is empty; discovered non-bundled plugins may auto-load: ...
    • Error: Bundled plugin public surface access blocked for "memory-core" via memory-core/runtime-api.js: disabled in config
  • Why it matters: The reporter has memory intentionally off and a real plugins.allow allowlist. Doctor should emit a clean informational result ("No active memory plugin is registered for the current config."), not fire warnings derived from a synthesized compat config. Runtime behavior is already correct; only the report was wrong.
  • What changed: Threaded cfg through the three getMemoryEmbeddingProvider call sites in src/agents/memory-search.ts so the capability-resolution chain sees the real config instead of falling back to a compat view that drops plugins.allow and plugins.entries.
  • What did NOT change (scope boundary): No loader changes (src/plugins/loader.ts untouched). No public SDK or config surface changes. getMemoryEmbeddingProvider already accepts an optional second cfg argument; this PR just starts passing it. Runtime memory-search behavior is unchanged.

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 #68088
  • Related #
  • This PR fixes a bug or regression

Root Cause (if applicable)

  • Root cause: mergeConfig in src/agents/memory-search.ts called getMemoryEmbeddingProvider without cfg at three sites. The capability-resolution chain (listMemoryEmbeddingProviders -> resolvePluginCapabilityProviders -> resolveRuntimePluginRegistry) falls back to a compat config view when cfg is missing. That compat view drops plugins.allow and plugins.entries, which makes the loader see an empty allowlist (first warning) and makes the memory-core activation guard see memory-core as effectively absent (second warning).
  • Missing detection / guardrail: The only call site for mergeConfig already had the real cfg in scope (as the outer parameter of resolveMemorySearchConfig), so threading it through was mechanical - there was no test asserting it actually got passed. This PR adds one.
  • Contributing context (if known): Reporter supplied a full trace stack, the root-cause analysis, and a verified diff:
    warnWhenAllowlistIsOpen (loader)
      loadOpenClawPlugins (loader)
      resolveRuntimePluginRegistry (loader)
      resolvePluginCapabilityProviders (capability-provider-runtime)
      listMemoryEmbeddingProviders (memory-embedding-provider-runtime)
      getMemoryEmbeddingProvider (memory-embedding-provider-runtime)
      mergeConfig (memory-search)
      resolveMemorySearchConfig (memory-search)
      noteMemorySearchHealth
    The reporter also confirmed locally that the patch eliminates both warnings and doctor ends with the correct informational result. Thanks to the reporter for the root-cause work.

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/agents/memory-search.test.ts
  • Scenario the test should lock in: When resolveMemorySearchConfig(cfg, agentId) runs with the reporter's disabled-memory config shape (plugins.allow: [], plugins.entries["memory-core"].enabled: false, plugins.slots.memory: "none", a non-auto provider), every getMemoryEmbeddingProvider call must receive the real cfg object rather than undefined.
  • Why this is the smallest reliable guardrail: The bug was a missing argument, not a missing feature. A spy on the provider lookup that asserts cfg is passed is the tightest possible coverage for this specific regression.
  • Existing test that already covers this (if any): None. src/agents/memory-search.test.ts had 23 tests before; this PR adds 1.
  • If no new test is added, why not: N/A. New test added (24 passing).

This contribution was developed with AI assistance (Codex).

Changed files

  • src/agents/memory-search.test.ts (modified, +33/-1)
  • src/agents/memory-search.ts (modified, +6/-4)

PR #68145: fix(memory-search): pass cfg to getMemoryEmbeddingProvider calls (#68088)

Description (problem / solution / changelog)

Summary

Fixes #68088: openclaw doctor --non-interactive emits two false positives when memory is intentionally disabled:

  1. plugins.allow is empty warning (spurious — plugins.allow is set in config)
  2. memory-core disabled in config error (spurious — doctor synthesizes a compat config that re-enables it)

Root Cause

Three getMemoryEmbeddingProvider() calls inside mergeConfig() and resolveMemorySearchConfig() were invoked without passing cfg. When cfg is omitted, the fallback path drops plugins.allow and plugins.entries, causing doctor to report incorrect state.

Fix

Thread cfg: OpenClawConfig through to all three getMemoryEmbeddingProvider() call sites in src/agents/memory-search.ts.

Testing

Run openclaw doctor --non-interactive with memory intentionally disabled via config. Both spurious warnings should be gone after this fix.

Changed files

  • extensions/memory-core/src/dreaming-narrative.ts (modified, +8/-3)
  • src/agents/memory-search.ts (modified, +5/-4)
  • src/agents/pi-embedded-runner/run/incomplete-turn.ts (modified, +2/-1)
  • src/agents/run-wait.ts (modified, +48/-0)
  • src/agents/tools/sessions-send-tool.ts (modified, +29/-0)
  • src/cli/plugins-update-selection.ts (modified, +5/-0)
  • src/cron/service/timer.ts (modified, +34/-12)
  • src/logging/subsystem.ts (modified, +2/-2)

Code Example

{
  "plugins": {
    "slots": {
      "contextEngine": "lossless-claw",
      "memory": "none"
    },
    "entries": {
      "memory-core": { "enabled": false },
      "lossless-claw": { "enabled": true }
    }
  }
}

---

openclaw doctor --non-interactive

---

[plugins] plugins.allow is empty; discovered non-bundled plugins may auto-load: camofox-browser (/root/.openclaw/extensions/camofox-browser/plugin.ts), composio (/root/.openclaw/extensions/composio/index.ts), lossless-claw (/root/.openclaw/extensions/lossless-claw/dist/index.js). Set plugins.allow to explicit trusted ids.

---

Error: Bundled plugin public surface access blocked for "memory-core" via memory-core/runtime-api.js: disabled in config

---

Trace: [allowlist-warn]
    at console.trace (file:///root/openclaw/dist/subsystem-BHv2zSx6.js:173:10)
    at warnWhenAllowlistIsOpen (file:///root/openclaw/dist/loader-DYJR63Q1.js:2174:10)
    at loadOpenClawPlugins (file:///root/openclaw/dist/loader-DYJR63Q1.js:2323:3)
    at resolveRuntimePluginRegistry (file:///root/openclaw/dist/loader-DYJR63Q1.js:1929:9)
    at resolvePluginCapabilityProviders (file:///root/openclaw/dist/capability-provider-runtime-Bnc80mfJ.js:43:10)
    at listMemoryEmbeddingProviders (file:///root/openclaw/dist/memory-embedding-provider-runtime-rR63PyF0.js:10:9)
    at getMemoryEmbeddingProvider (file:///root/openclaw/dist/memory-embedding-provider-runtime-rR63PyF0.js:19:9)
    at mergeConfig (file:///root/openclaw/dist/memory-search-BhL8gUVS.js:46:56)
    at resolveMemorySearchConfig (file:///root/openclaw/dist/memory-search-BhL8gUVS.js:196:19)
    at noteMemorySearchHealth (file:///root/openclaw/dist/prompt-select-styled-pEtizfU8.js:3513:19)

---

[allowlist-warn] keys [
  'emitWarning',
  'logger',
  'pluginsEnabled',
  'allow',
  'warningCacheKey',
  'discoverablePlugins'
]
[allowlist-warn] params.config: <missing>

---

diff --git a/src/agents/memory-search.ts b/src/agents/memory-search.ts
index <before>..<after> 100644
--- a/src/agents/memory-search.ts
+++ b/src/agents/memory-search.ts
@@ -143,20 +143,21 @@ function resolveStorePath(agentId: string, raw?: string): string {
 }
 
 function mergeConfig(
+  cfg: OpenClawConfig,
   defaults: MemorySearchConfig | undefined,
   overrides: MemorySearchConfig | undefined,
   agentId: string,
 ): ResolvedMemorySearchConfig {
   const enabled = overrides?.enabled ?? defaults?.enabled ?? true;
   const sessionMemory =
     overrides?.experimental?.sessionMemory ?? defaults?.experimental?.sessionMemory ?? false;
   const provider = overrides?.provider ?? defaults?.provider ?? "auto";
-  const primaryAdapter = provider === "auto" ? undefined : getMemoryEmbeddingProvider(provider);
+  const primaryAdapter = provider === "auto" ? undefined : getMemoryEmbeddingProvider(provider, cfg);
   const defaultRemote = defaults?.remote;
   const overrideRemote = overrides?.remote;
   const fallback = overrides?.fallback ?? defaults?.fallback ?? "none";
   const fallbackAdapter =
-    fallback && fallback !== "none" ? getMemoryEmbeddingProvider(fallback) : undefined;
+    fallback && fallback !== "none" ? getMemoryEmbeddingProvider(fallback, cfg) : undefined;
   const hasRemoteConfig = Boolean(
     overrideRemote?.baseUrl ||
     overrideRemote?.apiKey ||
@@ -382,13 +383,13 @@ export function resolveMemorySearchConfig(
 ): ResolvedMemorySearchConfig | null {
   const defaults = cfg.agents?.defaults?.memorySearch;
   const overrides = resolveAgentConfig(cfg, agentId)?.memorySearch;
-  const resolved = mergeConfig(defaults, overrides, agentId);
+  const resolved = mergeConfig(cfg, defaults, overrides, agentId);
   if (!resolved.enabled) {
     return null;
   }
   const multimodalActive = isMemoryMultimodalEnabled(resolved.multimodal);
   const multimodalProvider =
-    resolved.provider === "auto" ? undefined : getMemoryEmbeddingProvider(resolved.provider);
+    resolved.provider === "auto" ? undefined : getMemoryEmbeddingProvider(resolved.provider, cfg);
   const builtinMultimodalSupport =
     resolved.provider === "auto"
       ? false

---

openclaw doctor --non-interactive

---

No active memory plugin is registered for the current config.
RAW_BUFFERClick to expand / collapse

Environment

  • OpenClaw version: 2026.4.15
  • Commit: 4850cdc
  • Branch: update/v2026.4.15-local-fleet
  • Invocation: openclaw doctor --non-interactive
  • Runtime state: gateway healthy, RPC probe ok, runtime healthy

Scope

This is a doctor-only false-positive issue. Runtime behavior is unaffected.

The configuration below is intentional and correct. With memory intentionally disabled, doctor should produce a calm informational result, not warning/error output derived from a synthetic config view.

Repro

Configure ~/.openclaw/openclaw.json so memory is intentionally off:

{
  "plugins": {
    "slots": {
      "contextEngine": "lossless-claw",
      "memory": "none"
    },
    "entries": {
      "memory-core": { "enabled": false },
      "lossless-claw": { "enabled": true }
    }
  }
}

Then run:

openclaw doctor --non-interactive

Symptoms

Doctor emitted both of these during the same run:

Symptom 1

[plugins] plugins.allow is empty; discovered non-bundled plugins may auto-load: camofox-browser (/root/.openclaw/extensions/camofox-browser/plugin.ts), composio (/root/.openclaw/extensions/composio/index.ts), lossless-claw (/root/.openclaw/extensions/lossless-claw/dist/index.js). Set plugins.allow to explicit trusted ids.

Symptom 2

Error: Bundled plugin public surface access blocked for "memory-core" via memory-core/runtime-api.js: disabled in config

Root cause

src/agents/memory-search.ts calls getMemoryEmbeddingProvider(...) at three sites without threading the real cfg through. The capability-resolution chain that follows then synthesizes a compat config that drops plugins.allow and plugins.entries, which produces both observed symptoms.

Doctor reaches memory-search config resolution through:

  • noteMemorySearchHealth(...)
  • resolveMemorySearchConfig(...)
  • mergeConfig(...)

Within that path, getMemoryEmbeddingProvider(...) was called at these three sites without passing cfg:

  1. primary provider lookup in mergeConfig(...)
  2. fallback provider lookup in mergeConfig(...)
  3. multimodal provider lookup in resolveMemorySearchConfig(...)

That call chain descends through:

  • getMemoryEmbeddingProvider
  • listMemoryEmbeddingProviders
  • resolvePluginCapabilityProviders
  • resolveRuntimePluginRegistry

When cfg is omitted, capability resolution falls onto a compat-config path. That compat config drops both:

  • plugins.allow
  • plugins.entries

As a result:

  • the loader sees an empty allowlist and emits the plugins.allow is empty warning
  • the memory-core activation guard is consulted from a config view that never had the user’s real plugins.entries.memory-core.enabled setting to begin with, producing the misleading disabled in config error

Why both symptoms have the same root cause

The connection is causal, not incidental:

  • Allowlist warning path: compat config drops plugins.allow, so warnWhenAllowlistIsOpen(...) sees allow.length === 0
  • memory-core error path: compat config drops plugins.entries, so activation/public-surface checks run against an incomplete config view and treat memory-core as effectively absent/disabled

One missing cfg thread in the memory-search capability-resolution path caused both outputs.

Evidence: trace stack

Trace: [allowlist-warn]
    at console.trace (file:///root/openclaw/dist/subsystem-BHv2zSx6.js:173:10)
    at warnWhenAllowlistIsOpen (file:///root/openclaw/dist/loader-DYJR63Q1.js:2174:10)
    at loadOpenClawPlugins (file:///root/openclaw/dist/loader-DYJR63Q1.js:2323:3)
    at resolveRuntimePluginRegistry (file:///root/openclaw/dist/loader-DYJR63Q1.js:1929:9)
    at resolvePluginCapabilityProviders (file:///root/openclaw/dist/capability-provider-runtime-Bnc80mfJ.js:43:10)
    at listMemoryEmbeddingProviders (file:///root/openclaw/dist/memory-embedding-provider-runtime-rR63PyF0.js:10:9)
    at getMemoryEmbeddingProvider (file:///root/openclaw/dist/memory-embedding-provider-runtime-rR63PyF0.js:19:9)
    at mergeConfig (file:///root/openclaw/dist/memory-search-BhL8gUVS.js:46:56)
    at resolveMemorySearchConfig (file:///root/openclaw/dist/memory-search-BhL8gUVS.js:196:19)
    at noteMemorySearchHealth (file:///root/openclaw/dist/prompt-select-styled-pEtizfU8.js:3513:19)

And the parameter shape at the warning site showed no config present there:

[allowlist-warn] keys [
  'emitWarning',
  'logger',
  'pluginsEnabled',
  'allow',
  'warningCacheKey',
  'discoverablePlugins'
]
[allowlist-warn] params.config: <missing>

Proposed fix

Thread the existing cfg through the three getMemoryEmbeddingProvider(...) call sites in src/agents/memory-search.ts.

No downstream signature changes are required. No loader/runtime global semantics are touched.

diff --git a/src/agents/memory-search.ts b/src/agents/memory-search.ts
index <before>..<after> 100644
--- a/src/agents/memory-search.ts
+++ b/src/agents/memory-search.ts
@@ -143,20 +143,21 @@ function resolveStorePath(agentId: string, raw?: string): string {
 }
 
 function mergeConfig(
+  cfg: OpenClawConfig,
   defaults: MemorySearchConfig | undefined,
   overrides: MemorySearchConfig | undefined,
   agentId: string,
 ): ResolvedMemorySearchConfig {
   const enabled = overrides?.enabled ?? defaults?.enabled ?? true;
   const sessionMemory =
     overrides?.experimental?.sessionMemory ?? defaults?.experimental?.sessionMemory ?? false;
   const provider = overrides?.provider ?? defaults?.provider ?? "auto";
-  const primaryAdapter = provider === "auto" ? undefined : getMemoryEmbeddingProvider(provider);
+  const primaryAdapter = provider === "auto" ? undefined : getMemoryEmbeddingProvider(provider, cfg);
   const defaultRemote = defaults?.remote;
   const overrideRemote = overrides?.remote;
   const fallback = overrides?.fallback ?? defaults?.fallback ?? "none";
   const fallbackAdapter =
-    fallback && fallback !== "none" ? getMemoryEmbeddingProvider(fallback) : undefined;
+    fallback && fallback !== "none" ? getMemoryEmbeddingProvider(fallback, cfg) : undefined;
   const hasRemoteConfig = Boolean(
     overrideRemote?.baseUrl ||
     overrideRemote?.apiKey ||
@@ -382,13 +383,13 @@ export function resolveMemorySearchConfig(
 ): ResolvedMemorySearchConfig | null {
   const defaults = cfg.agents?.defaults?.memorySearch;
   const overrides = resolveAgentConfig(cfg, agentId)?.memorySearch;
-  const resolved = mergeConfig(defaults, overrides, agentId);
+  const resolved = mergeConfig(cfg, defaults, overrides, agentId);
   if (!resolved.enabled) {
     return null;
   }
   const multimodalActive = isMemoryMultimodalEnabled(resolved.multimodal);
   const multimodalProvider =
-    resolved.provider === "auto" ? undefined : getMemoryEmbeddingProvider(resolved.provider);
+    resolved.provider === "auto" ? undefined : getMemoryEmbeddingProvider(resolved.provider, cfg);
   const builtinMultimodalSupport =
     resolved.provider === "auto"
       ? false

Verification

Verification performed locally:

  1. applied the diff above
  2. rebuilt the project/runtime artifact
  3. reran:
openclaw doctor --non-interactive

Result:

  • plugins.allow is empty warning: gone
  • memory-core ... disabled in config error: gone
  • no new doctor errors introduced

End state is now the clean informational message:

No active memory plugin is registered for the current config.

That is the correct result for this intentional configuration.

Suggested regression tests

Add regression coverage for the disabled-memory fixture above so that:

  1. openclaw doctor --non-interactive does not emit:
    • plugins.allow is empty
    • Bundled plugin public surface access blocked for "memory-core"...
  2. doctor instead ends with the informational memory result:
    • No active memory plugin is registered for the current config.

Optional unit-level coverage:

  • verify resolveMemorySearchConfig(cfg, agentId) threads cfg through provider lookups for:
    • primary provider
    • fallback provider
    • multimodal provider path

extent analysis

TL;DR

The proposed fix involves threading the existing cfg through the three getMemoryEmbeddingProvider(...) call sites in src/agents/memory-search.ts to resolve the false-positive issue.

Guidance

  • Apply the provided diff to src/agents/memory-search.ts to thread the cfg through the getMemoryEmbeddingProvider calls.
  • Rebuild the project/runtime artifact after applying the diff.
  • Rerun openclaw doctor --non-interactive to verify the fix.
  • Add regression tests to cover the disabled-memory fixture and ensure the doctor output is correct.

Example

The provided diff demonstrates the necessary changes to src/agents/memory-search.ts:

diff --git a/src/agents/memory-search.ts b/src/agents/memory-search.ts
index <before>..<after> 100644
--- a/src/agents/memory-search.ts
+++ b/src/agents/memory-search.ts
@@ -143,20 +143,21 @@ function resolveStorePath(agentId: string, raw?: string): string {
 }
 
 function mergeConfig(
+  cfg: OpenClawConfig,
   defaults: MemorySearchConfig | undefined,
   overrides: MemorySearchConfig | undefined,
   agentId: string,
 ): ResolvedMemorySearchConfig {
   ...

Notes

The fix only requires changes to src/agents/memory-search.ts and does not affect downstream signatures or loader/runtime global semantics.

Recommendation

Apply the proposed fix by threading the cfg through the getMemoryEmbeddingProvider calls, as it directly addresses the root cause of the issue and has been verified to resolve the false-positive 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…

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 openclaw doctor --non-interactive emits two false positives when memory is intentionally disabled: plugins.allow is empty warning and memory-core disabled in config error [2 pull requests, 1 comments, 2 participants]