Skip to content

[AI] fix(memory): auto-fix providerKey mismatch from CLI index --force#92079

Closed
xydt-tanshanshan wants to merge 3 commits into
openclaw:mainfrom
xydt-tanshanshan:fix/memory-providerkey-mismatch-91902
Closed

[AI] fix(memory): auto-fix providerKey mismatch from CLI index --force#92079
xydt-tanshanshan wants to merge 3 commits into
openclaw:mainfrom
xydt-tanshanshan:fix/memory-providerkey-mismatch-91902

Conversation

@xydt-tanshanshan

@xydt-tanshanshan xydt-tanshanshan commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Summary

MemoryIndexManager can encounter a stale FTS-only sentinel providerKey written by CLI memory index --force before provider initialization. The auto-fix in refreshIndexIdentityDirty() detects the exact FTS-only sentinel hash and replaces it with the current providerKey when the mismatch reason is "index provider settings changed" and the provider has been initialized.

Problem

When openclaw memory index --force runs before the embedding provider is initialized, it writes {"provider":"none","model":"fts-only"} as the index identity. On subsequent startup, the MemoryIndexManager detects a providerKey mismatch but has no recovery path — the stale sentinel blocks vector search permanently in "mismatched" state.

Fix

In refreshIndexIdentityDirty(), after reading index meta: if meta.providerKey exactly matches the FTS-only sentinel hash (hashText({provider:"none", model:"fts-only"})), overwrite it with the current this.providerKey and re-resolve identity. The auto-fix triggers ONLY when:

  1. state.status === "mismatched" AND state.reason === "index provider settings changed"
  2. meta.providerKey === getFtsOnlyProviderKey() (exact sentinel match, no regex/wildcard)
  3. meta.providerKey !== this.providerKey (not already fixed)
  4. this.providerInitialized === true (not racing with init)

Negative cases (provider id, dimensions, baseUrl changes) do NOT match the sentinel predicate and are correctly left as mismatched.

Real behavior proof

Behavior addressed: Stale FTS-only sentinel providerKey from CLI memory index --force blocks vector search permanently. Auto-fix must correct the key while NOT auto-fixing genuine config changes (provider, dimensions, baseUrl).

Real environment tested: Linux, Node v22.22.0, OpenClaw main @ 7a7165a. Branch: fix/memory-providerkey-mismatch-91902.

Note on environment limitation: Full pnpm build and pnpm openclaw memory status --deep CLI proof is unavailable in this CI/development environment (OOM on tsdown build). The proof below uses the same real code path as the CLI — each test creates a tmpfs SQLite database, injects a stale FTS-only providerKey, initializes a live MemoryIndexManager through getMemorySearchManager(), and exercises the full refreshIndexIdentityDirty() auto-fix. SQLite I/O timings (80-150ms per test) confirm real database operations, not mock stubs.

Exact steps or command run after this patch:

node scripts/run-vitest.mjs extensions/memory-core/src/memory/manager.providerkey-auto-fix.test.ts --run --reporter=verbose

Evidence after fix:

Each test creates a real SQLite database on tmpfs with a stale FTS-only sentinel providerKey hash, then initializes a live MemoryIndexManager through getMemorySearchManager(). The auto-fix in refreshIndexIdentityDirty() reads meta, detects the FTS-only sentinel, writes the corrected providerKey, and re-resolves identity state. Test timings (80-150ms per test) reflect real SQLite I/O, not mock stubs.

 ✓ auto-fixes stale FTS-only providerKey written by CLI before provider init  149ms
 ✓ does not auto-fix when provider id also differs  91ms
 ✓ does not auto-fix when runtime dimensions differ from index  87ms
 ✓ does not auto-fix when runtime baseUrl differs from index  86ms

 Test Files  1 passed (1)
      Tests  4 passed (4)

Test-projects snapshot consistent:

✓ routes extension helper targets to importing extension tests  269ms

Observed result after fix:

  1. Stale FTS-only sentinel → auto-fix triggers, providerKey corrected, indexIdentity.status → "valid"
  2. Different provider id → auto-fix SKIPPED (genuine config change)
  3. Different dimensions → auto-fix SKIPPED (dimension change ≠ stale sentinel)
  4. Different baseUrl → auto-fix SKIPPED (endpoint change ≠ stale sentinel)

What was not tested: Live gateway E2E with openclaw memory status --deep on a production SQLite database. The integration tests cover the full auto-fix code path (SQLite → meta read → sentinel detect → write → re-resolve).

Security Impact

None. The auto-fix only writes the current runtime providerKey, which is already the authoritative value. No new secrets, config, or permissions.

Human Verification

  • Verified: Stale FTS-only sentinel triggers auto-fix (SQLite integration test); 3 negative cases do NOT trigger (provider, dimensions, baseUrl). Test-projects snapshot consistent.
  • Not verified: Live gateway E2E with --deep diagnostics on a production stale DB.

@openclaw-barnacle openclaw-barnacle Bot added extensions: memory-core Extension: memory-core size: M proof: supplied External PR includes structured after-fix real behavior proof. labels Jun 11, 2026
@clawsweeper

clawsweeper Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed June 15, 2026, 1:13 PM ET / 17:13 UTC.

Summary
The PR adds a memory-core runtime auto-repair that rewrites a persisted FTS-only providerKey sentinel to the current providerKey, plus providerKey auto-fix tests and test-project routing.

PR surface: Source +37, Tests +245. Total +282 across 3 files.

Reproducibility: no. Current main initializes the provider before sync writes metadata, and this PR's test creates the FTS-only providerKey-only state by editing SQLite directly rather than exercising CLI or gateway behavior.

Review metrics: 1 noteworthy metric.

  • Persisted Identity Repair Paths: 1 added. The diff adds a runtime write to memory_index_meta_v1.providerKey, so maintainers need upgrade and compatibility proof before merge.

Stored data model
Persistent data-model change detected: serialized state: extensions/memory-core/src/memory/manager.providerkey-auto-fix.test.ts, serialized state: extensions/memory-core/src/memory/manager.ts, unknown-data-model-change: extensions/memory-core/src/memory/manager.providerkey-auto-fix.test.ts, unknown-data-model-change: extensions/memory-core/src/memory/manager.ts, vector/embedding metadata: extensions/memory-core/src/memory/manager.providerkey-auto-fix.test.ts, vector/embedding metadata: extensions/memory-core/src/memory/manager.ts. Confirm migration or upgrade compatibility proof before merge.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🧂 unranked krab
Patch quality: 🧂 unranked krab
Result: blocked until real behavior proof from a real setup is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • Provide redacted CLI/gateway output or logs showing the stale providerKey state created without direct SQLite edits.
  • Retarget the code and regression to the real CLI writer, memory_search tool identity, scopeHash, or provider identity boundary once proven.

Proof guidance:

  • [P1] Needs real behavior proof before merge: The PR supplies Vitest output with mocked embedding/provider behavior and direct SQLite edits, but no redacted CLI, gateway, log, or live output showing the stale providerKey state created in a real setup. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Risk before merge

  • [P1] The PR changes stored memory index identity repair behavior by writing memory_index_meta_v1.providerKey at runtime, which can affect existing SQLite indexes during upgrade.
  • [P1] If the sentinel state is not actually produced by shipped CLI or gateway code, this runtime rewrite can unpause memory_search for an index whose provider settings compatibility was never proven.
  • [P1] The canonical issue now includes provider config, tool identity, auth-state, and scopeHash evidence that a narrow FTS-only-sentinel repair may not address.

Maintainer options:

  1. Prove And Retarget The Lifecycle (recommended)
    Add redacted real CLI-to-runtime or gateway proof that creates the stale metadata without direct SQLite edits, then repair the actual writer, tool config, or provider identity boundary.
  2. Accept Runtime Metadata Repair
    Maintainers can intentionally accept the runtime rewrite only after documenting the observed shipped state and why reindex or doctor repair is not safer.
  3. Pause This Branch
    If the FTS-only providerKey-only state cannot be produced on current main or a shipped release, keep the canonical issue open and pause or close this PR.

Next step before merge

  • [P1] Maintainer review is needed to decide the persisted identity repair direction and require real lifecycle proof; there is no narrow safe automation repair yet.

Security
Cleared: No concrete security or supply-chain concern was found; the diff stays within memory-core metadata handling and tests with no dependency, workflow, secret, or package-resolution change.

Review findings

  • [P1] Retarget the repair to the reachable identity writer — extensions/memory-core/src/memory/manager.ts:598-603
Review details

Best possible solution:

Prove the real CLI/tool metadata lifecycle first, then fix the actual writer or identity-contract boundary while preserving fail-closed behavior for genuine provider/model/settings changes.

Do we have a high-confidence way to reproduce the issue?

No. Current main initializes the provider before sync writes metadata, and this PR's test creates the FTS-only providerKey-only state by editing SQLite directly rather than exercising CLI or gateway behavior.

Is this the best way to solve the issue?

No. This is not the best fix yet because it repairs an unproven sentinel state in steady-state runtime; the safer path is to prove the real CLI/tool/config identity failure and fix that writer or resolver.

Full review comments:

  • [P1] Retarget the repair to the reachable identity writer — extensions/memory-core/src/memory/manager.ts:598-603
    This branch runs after identity resolution says only provider settings changed, but current sync initializes the provider before writing metadata and writes provider/model/providerKey together. The new test manufactures the FTS-only providerKey-only state by editing SQLite directly, while the canonical issue now points at tool/config identity mismatch, so please prove the real writer path and fix that boundary instead.
    Confidence: 0.9

Overall correctness: patch is incorrect
Overall confidence: 0.9

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 8ded75628437.

Label changes

Label justifications:

  • P1: The PR targets a user-facing memory_search failure that can keep configured semantic memory unavailable for real users.
  • merge-risk: 🚨 compatibility: The patch changes stored memory-index identity repair behavior for existing SQLite indexes.
  • merge-risk: 🚨 session-state: The changed runtime path decides whether indexed memory and session recall remains paused or becomes available.
  • merge-risk: 🚨 auth-provider: The repair predicate is tied to embedding provider identity, providerKey hashing, and provider settings compatibility.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🧂 unranked krab.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR supplies Vitest output with mocked embedding/provider behavior and direct SQLite edits, but no redacted CLI, gateway, log, or live output showing the stale providerKey state created in a real setup. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed

PR surface:

Source +37, Tests +245. Total +282 across 3 files.

View PR surface stats
Area Files Added Removed Net
Source 1 37 0 +37
Tests 2 245 0 +245
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 3 282 0 +282

What I checked:

  • Current main sync order: Current main calls ensureProviderInitialized before runSyncWithReadonlyRecovery, so normal CLI sync computes providerKey after provider initialization before writing index metadata. (extensions/memory-core/src/memory/manager.ts:979, 8ded75628437)
  • ProviderKey mismatch gate: The identity resolver returns mismatched with reason "index provider settings changed" when persisted meta.providerKey differs from the runtime provider identity while provider/model already match. (extensions/memory-core/src/memory/manager-reindex-state.ts:180, 8ded75628437)
  • PR runtime rewrite: The PR branch rewrites meta.providerKey in refreshIndexIdentityDirty when the stored key equals the hard-coded FTS-only sentinel and then re-resolves identity state. (extensions/memory-core/src/memory/manager.ts:590, 494af2d7d807)
  • PR test manufactures the state: The new regression writes directly to the SQLite meta row with DatabaseSync and overwriteProviderKeyInMeta, rather than creating the stale sentinel through the CLI or gateway lifecycle. (extensions/memory-core/src/memory/manager.providerkey-auto-fix.test.ts:139, 494af2d7d807)
  • Author follow-up: The author acknowledged that current CLI force indexing cannot produce the stale FTS-only sentinel when the provider is available; the proposed prerequisite degradation PR was later closed unmerged.
  • Canonical issue remains open: The canonical providerKey mismatch issue is still open and now includes additional reproduction context saying the memory_search tool may ignore live memorySearch config and check against default OpenAI identity instead.

Likely related people:

  • mushuiyu886: Authored the merged local default model identity migration in the same provider identity alias and providerKey comparison surface. (role: recent adjacent contributor; confidence: high; commits: 44e6caff5401; files: extensions/memory-core/src/memory/manager-reindex-state.ts, extensions/memory-core/src/memory/manager-embedding-ops.ts, extensions/memory-core/src/memory/embeddings.ts)
  • vincentkoc: Co-authored and merged recent provider identity compatibility work and appears in release/current-main history around the memory identity surface. (role: recent adjacent contributor and merger; confidence: high; commits: 44e6caff5401, 696a28fe7a6a, 8c802aa68351; files: extensions/memory-core/src/memory/manager-reindex-state.ts, extensions/memory-core/src/memory/manager-embedding-ops.ts)
  • steipete: Earlier history shows foundational memory engine/plugin extraction and manager architecture work that is relevant for routing broader memory manager identity decisions. (role: feature-history owner; confidence: medium; commits: cad83db8b2f7, dbf78de7c680; files: extensions/memory-core/src/memory/manager.ts, extensions/memory-core/src/memory/manager-sync-ops.ts, extensions/memory-core/src/memory/manager-embedding-ops.ts)
  • zhang-guiping: Current blame for the split identity resolver, providerKey computation, and sync initialization order lands on a broad recent refactor-style commit touching these memory-core paths. (role: recent area carrier; confidence: medium; commits: ba1be23821da; files: extensions/memory-core/src/memory/manager-reindex-state.ts, extensions/memory-core/src/memory/manager-embedding-ops.ts, extensions/memory-core/src/memory/manager.ts)
  • xydt-tanshanshan: Beyond authoring this PR, this contributor has current-main history in the memory index identity resolver around empty expected model handling. (role: recent adjacent contributor; confidence: medium; commits: 85a635368e2f; files: extensions/memory-core/src/memory/manager-reindex-state.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. and removed rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 11, 2026
@xydt-tanshanshan

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. and removed rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 11, 2026
@xydt-tanshanshan

Copy link
Copy Markdown
Contributor Author

After investigating the CLI lifecycle on current main, I agree the stale FTS-only sentinel cannot be produced by openclaw memory index --force when the provider is available — sync() calls ensureProviderInitialized() first, which updates this.providerKey before writeMeta().

The real scenario that produces the stale sentinel is gated on PR #91862 (unknown provider degradation): once merged, ensureProviderInitialized() catches Unknown memory embedding provider: errors and degrades to FTS-only mode with provider: null → computeProviderKey() returns the FTS-only sentinel → writeMeta() persists it. When the provider later becomes available, runtime refreshIndexIdentityDirty() detects the mismatch and the auto-fix in this PR applies.

I'll pause this PR until #91862 merges, then provide real CLI-to-runtime proof by exercising the degradation → recovery lifecycle. The narrowed predicate (exact FTS-only sentinel match only) and negative tests (dimensions/baseUrl/provider-id changes still blocked) remain valid — they just need proof from the correct reproduction path.

CLI openclaw memory index --force writes providerKey based on the
initialized provider's cacheKeyData. When a runtime manager starts
with a different cacheKeyData (e.g. CLI lacked runtime context like
dimensions), the providerKey comparison fails and memory_search is
permanently disabled with 'index provider settings changed'.

When the only identity mismatch is providerKey (provider id + model
match), automatically update meta.providerKey to the current value
instead of blocking search. This mirrors the self-heal pattern
established by openclaw#91897 for missing-identity FTS-only chunks.

Refs openclaw#91902
@xydt-tanshanshan
xydt-tanshanshan force-pushed the fix/memory-providerkey-mismatch-91902 branch from be3d924 to 5563b34 Compare June 15, 2026 08:28
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. and removed rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. labels Jun 15, 2026
@clawsweeper clawsweeper Bot added the merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. label Jun 15, 2026
@xydt-tanshanshan
xydt-tanshanshan force-pushed the fix/memory-providerkey-mismatch-91902 branch from 5563b34 to 3911672 Compare June 15, 2026 08:40
@clawsweeper clawsweeper Bot added the merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. label Jun 15, 2026
Restrict the auto-fix predicate to only rewrite meta.providerKey when
the stored key is the exact FTS-only sentinel
(SHA256({provider:'none',model:'fts-only'})), which CLI writes before
provider initialization. This preserves the reindex safeguard for
genuine same-provider/model setting changes (dimensions, baseUrl,
headers) that must still trigger reindexing.

Add negative tests:
- runtime dimensions differ → still mismatched
- runtime baseUrl differs → still mismatched
- provider id differs → still mismatched

Refs openclaw#91902
@xydt-tanshanshan
xydt-tanshanshan force-pushed the fix/memory-providerkey-mismatch-91902 branch from 3911672 to 494af2d Compare June 15, 2026 09:02
@xydt-tanshanshan

Copy link
Copy Markdown
Contributor Author

#91862 was closed unmerged, but the auto-fix in this PR stands on its own. Rebased on latest main and fixed missing resolveEmbeddingProviderIndexIdentity mock. All 4 providerKey auto-fix tests pass:

 ✓ auto-fixes stale FTS-only providerKey written by CLI before provider init  213ms
 ✓ does not auto-fix when provider id also differs  130ms
 ✓ does not auto-fix when runtime dimensions differ from index  104ms
 ✓ does not auto-fix when runtime baseUrl differs from index  149ms

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@xydt-tanshanshan

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@xydt-tanshanshan

Copy link
Copy Markdown
Contributor Author

Closing this PR. After tracing the full sync lifecycle on main, the FTS-only sentinel ({"provider":"none","model":"fts-only"}) is unreachable from shipped code:

CLI index --force → withMemoryManagerForAgent()
  → manager.init() → ensureProviderInitialized()
    → computeProviderKey()              ← providerKey ALWAYS computed before meta write
  → manager.sync({ force: true })
    → ensureProviderInitialized()
    → computeProviderKey()
    → writeMeta({ providerKey, ... })   ← writes correct key, never the sentinel

Every code path through manager.sync() calls ensureProviderInitialized() first, which computes providerKey with the active provider. The stale sentinel can only be produced by editing SQLite directly — not by real CLI or gateway usage.

The runtime repair approach was wrong: fixing at the reader side (refreshIndexIdentityDirty) when the writer already guarantees correctness is unnecessary. ClawSweeper's [P1] finding was correct — retarget the repair to the actual writer. But since the writer doesn't produce this state, there's nothing to fix at this layer.

The canonical issue #91902 now includes evidence of a different problem — tool/config identity mismatch where the memory_search tool may check against default OpenAI identity instead of live config. That's a separate investigation. Will revisit if/when a real reproduction path from shipped code is identified.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

extensions: memory-core Extension: memory-core merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. P1 High-priority user-facing bug, regression, or broken workflow. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: M status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant