fix(memory): self-heal missing index identity by initializing provider during sync#91897
Conversation
|
Codex review: needs real behavior proof before merge. Reviewed June 10, 2026, 5:37 AM ET / 09:37 UTC. Summary PR surface: Source +22, Tests +140. Total +162 across 2 files. Reproducibility: yes. from source: current main leaves missing identity dirty when chunks exist, the provider is unavailable, and the configured provider is not Review metrics: none identified. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the narrow FTS-only missing-identity fix after proof shows the configured-provider-unavailable branch works; keep the stale DB handle and semantic-unavailable cases tracked on #91167. Do we have a high-confidence way to reproduce the issue? Yes from source: current main leaves missing identity dirty when chunks exist, the provider is unavailable, and the configured provider is not Is this the best way to solve the issue? Yes for the scoped FTS-only slice: the change is in the existing missing-identity decision point and preserves semantic chunks. The proposed branch still needs proof for the actual configured-provider-unavailable scenario before merge. AGENTS.md: found and applied where relevant. Codex review notes: model gpt-5.5, reasoning high; reviewed against c84e52192063. Label changesLabel justifications:
Evidence reviewedPR surface: Source +22, Tests +140. Total +162 across 2 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
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 re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
@clawsweeper re-review |
|
🦞👀 Command router queued. I will update this comment with the next step. |
…ovider during sync When a gateway's memory index identity becomes "missing" with chunks already indexed, canRebuildMissingIdentity stays false because this.provider is null (async provider init hasn completed yet), so needsMissingIdentityReindex is false, and the sync loop bails out with dirty=true forever — the gateway never self-heals. Now, when indexIdentity.status is "missing" and a provider is configured but this.provider is null, runSync() calls ensureProviderInitialized() first, then re-evaluates the identity state. If the provider becomes available, canRebuildMissingIdentity flips to true, unlocking the self-heal reindex path. Refs openclaw#91167
…ly and provider unavailable When a gateway's memory index identity is 'missing' with chunks already indexed, canRebuildMissingIdentity stays false if the embedding provider is unavailable, causing the sync loop to bail out with dirty=true forever. The previous approach (calling ensureProviderInitialized inside runSync) was redundant because the public sync() method already initializes the provider before runSyncWithReadonlyRecovery. The real fix: when every existing chunk has model='fts-only', rebuilding the index as FTS-only is safe — no semantic data is lost. So canRebuildMissingIdentity should also be true when hasOnlyFtsChunks, even if the provider is unavailable. Also adds hasSemanticChunks() helper to detect whether any chunks have a non-fts-only model. Non-forced test: seeds FTS-only chunks with no meta, syncs without force, verifies identity transitions from 'missing' to 'valid'. Refs openclaw#91167
…h only Only compute hasOnlyFtsChunks when identity is missing, chunks exist, and the provider is unavailable. This avoids scanning the chunks table for model classification on every ordinary sync.
|
Maintainer review complete. The self-heal remains fail-closed for semantic data: only a missing-identity index whose stored chunks are all Verification:
GitHub CI on the updated SHA is the broad gate because local Blacksmith CLI and Crabbox broker auth are unavailable. Updated head: 086e478 |
974fb8c to
086e478
Compare
…r during sync (openclaw#91897) * [AI] fix(memory): self-heal missing index identity by initializing provider during sync When a gateway's memory index identity becomes "missing" with chunks already indexed, canRebuildMissingIdentity stays false because this.provider is null (async provider init hasn completed yet), so needsMissingIdentityReindex is false, and the sync loop bails out with dirty=true forever — the gateway never self-heals. Now, when indexIdentity.status is "missing" and a provider is configured but this.provider is null, runSync() calls ensureProviderInitialized() first, then re-evaluates the identity state. If the provider becomes available, canRebuildMissingIdentity flips to true, unlocking the self-heal reindex path. Refs openclaw#91167 * [AI] fix(memory): allow FTS-only self-heal when chunks are all FTS-only and provider unavailable When a gateway's memory index identity is 'missing' with chunks already indexed, canRebuildMissingIdentity stays false if the embedding provider is unavailable, causing the sync loop to bail out with dirty=true forever. The previous approach (calling ensureProviderInitialized inside runSync) was redundant because the public sync() method already initializes the provider before runSyncWithReadonlyRecovery. The real fix: when every existing chunk has model='fts-only', rebuilding the index as FTS-only is safe — no semantic data is lost. So canRebuildMissingIdentity should also be true when hasOnlyFtsChunks, even if the provider is unavailable. Also adds hasSemanticChunks() helper to detect whether any chunks have a non-fts-only model. Non-forced test: seeds FTS-only chunks with no meta, syncs without force, verifies identity transitions from 'missing' to 'valid'. Refs openclaw#91167 * [AI] fix(memory): gate hasSemanticChunks scan to missing-identity path only Only compute hasOnlyFtsChunks when identity is missing, chunks exist, and the provider is unavailable. This avoids scanning the chunks table for model classification on every ordinary sync. * test(memory): protect semantic index self-heal --------- Co-authored-by: Vincent Koc <[email protected]>
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
…r during sync (openclaw#91897) * [AI] fix(memory): self-heal missing index identity by initializing provider during sync When a gateway's memory index identity becomes "missing" with chunks already indexed, canRebuildMissingIdentity stays false because this.provider is null (async provider init hasn completed yet), so needsMissingIdentityReindex is false, and the sync loop bails out with dirty=true forever — the gateway never self-heals. Now, when indexIdentity.status is "missing" and a provider is configured but this.provider is null, runSync() calls ensureProviderInitialized() first, then re-evaluates the identity state. If the provider becomes available, canRebuildMissingIdentity flips to true, unlocking the self-heal reindex path. Refs openclaw#91167 * [AI] fix(memory): allow FTS-only self-heal when chunks are all FTS-only and provider unavailable When a gateway's memory index identity is 'missing' with chunks already indexed, canRebuildMissingIdentity stays false if the embedding provider is unavailable, causing the sync loop to bail out with dirty=true forever. The previous approach (calling ensureProviderInitialized inside runSync) was redundant because the public sync() method already initializes the provider before runSyncWithReadonlyRecovery. The real fix: when every existing chunk has model='fts-only', rebuilding the index as FTS-only is safe — no semantic data is lost. So canRebuildMissingIdentity should also be true when hasOnlyFtsChunks, even if the provider is unavailable. Also adds hasSemanticChunks() helper to detect whether any chunks have a non-fts-only model. Non-forced test: seeds FTS-only chunks with no meta, syncs without force, verifies identity transitions from 'missing' to 'valid'. Refs openclaw#91167 * [AI] fix(memory): gate hasSemanticChunks scan to missing-identity path only Only compute hasOnlyFtsChunks when identity is missing, chunks exist, and the provider is unavailable. This avoids scanning the chunks table for model classification on every ordinary sync. * test(memory): protect semantic index self-heal --------- Co-authored-by: Vincent Koc <[email protected]>
Summary
When a gateway's memory index identity is
"missing"with chunks already indexed, the gateway never self-heals on its own if the embedding provider is unavailable.canRebuildMissingIdentitystaysfalsebecausethis.provider === null, soneedsMissingIdentityReindexisfalse, and the sync loop bails out withdirty=trueforever.The real fix: when every existing chunk has
model='fts-only', rebuilding the index as FTS-only is safe — no semantic data is lost. SocanRebuildMissingIdentityshould also betruewhenhasOnlyFtsChunks, even if the provider is unavailable.A previous approach (calling
ensureProviderInitialized()insiderunSync()) was rejected by Codex review as redundant because the publicsync()method already initializes the provider beforerunSyncWithReadonlyRecovery(). This revision targets the actual missing-identity reindex decision directly.This addresses the FTS-only chunk portion of #91167. The broader stale-DB-handle problem (CLI atomic swap not reaching the running gateway) and the semantic-chunk-with-unavailable-provider scenario remain outside this diff.
Change Type
Scope
Linked Issue
Motivation
Issue #91167 reports that a long-running gateway whose memory index-identity becomes
"missing"with chunks already indexed never recovers on its own. When the embedding provider is unavailable (unregistered plugin, auth failure, network error),canRebuildMissingIdentity = this.provider !== null || !this.settings.provider || this.settings.provider === "none"evaluates tofalse, soneedsMissingIdentityReindex = false, and the sync loop returns early withdirty=trueforever.However, when every existing chunk has
model='fts-only', there is no semantic vector data at risk. Rebuilding the index as FTS-only restores the metadata without losing any information. The current guard is too conservative for this case.Changes
File:
extensions/memory-core/src/memory/manager-sync-ops.tsAdded
hasSemanticChunks()helper (line 400): queriesSELECT 1 FROM chunks WHERE model != 'fts-only' LIMIT 1to detect whether any chunks have a non-FTS-only model.Modified
canRebuildMissingIdentitylogic (line 2080): added|| hasOnlyFtsChunkswherehasOnlyFtsChunks = this.hasIndexedChunks() && !this.hasSemanticChunks(). When all indexed chunks are FTS-only, rebuilding as FTS-only is safe even without a provider.This is a narrow additive change: one new
protectedmethod + one additional boolean condition. No config, default, DB handle, or provider-init changes. TheassertFtsOnlySyncAllowed()guard at line 2043 already allows sync whenexistingMeta === null(line 2018), so no additional guard changes are needed.File:
extensions/memory-core/src/memory/manager.self-heal-missing-identity.test.ts(new)1 test covering the non-forced self-heal path: seeds FTS-only chunks with no meta, creates a manager with
provider: "auto"andprovider: null(unavailable), syncs without force, verifies thatindexIdentity.statustransitions from"missing"to"valid"anddirtybecomesfalse.Real Behavior Proof
"missing"with FTS-only chunks already indexed never self-heals when the embedding provider is unavailable, becausecanRebuildMissingIdentitystaysfalseand the sync loop bails out withdirty=trueforever.fix/memory-self-heal-missing-identity-91167at commit7621e7d04, dev build vianode scripts/run-node.mjs.~/.openclaw/memory/main.sqlitewith nomemory_index_meta_v1row (simulating bug(memory): gateway cannot self-heal a missing index identity when chunks are already indexed #91167's "chunks exist but meta missing" state), then runnode scripts/run-node.mjs memory status(non-forced, simulating gateway periodic sync)provider: "auto"withprovider: null(unavailable), syncs without force, and verifies identity transitions from"missing"to"valid"anddirtybecomesfalse:Live output with
provider: "none"(already-supported path, confirms the rebuild mechanism works end-to-end):SQLite meta after self-heal:
Live output with
provider: "auto"→ resolved to"openai"→ unavailable (Invalid URL), confirming the newhasOnlyFtsChunksbranch activates when a non-none provider is configured but unavailable:Note: The live run with a non-none unavailable provider did not complete self-heal in this run because the CLI
memory status --indexusespurpose: "cli"which triggersneedsExplicitIdentityReindex, and the full reindex requires a working embedding endpoint. ThehasOnlyFtsChunkspath correctly setsneedsMissingIdentityReindex = trueandneedsFullReindex = true, but the subsequent reindex attempts to re-embed files and hits the provider error. This is expected: the gateway's periodic non-forced sync would trigger the same path but the FTS-only rebuild (without embedding) should succeed since no semantic chunks exist. The unit test proves the identity decision works correctly; the live embedding failure is an environment limitation.Note on proof dependency: A live run proving end-to-end self-heal with a non-none unavailable provider requires PR #91862 (unknown provider degradation fix) to be merged first, so that the CLI does not crash when the provider is unregistered. Once #91862 is merged, the live proof can be refreshed.
model='fts-only',canRebuildMissingIdentitynow includeshasOnlyFtsChunks=true, allowing the self-heal reindex path to activate on a non-forced gateway periodic sync. The index identity transitions from"missing"to"valid"without requiring a provider. Semantic chunks (model != 'fts-only') are still protected: if any semantic chunk exists,hasOnlyFtsChunks=falseandcanRebuildMissingIdentityrequires the provider to be available.Risks
hasSemanticChunks()is a read-only query that does not modify any state. ThehasOnlyFtsChunkscondition only adds one moretruecase tocanRebuildMissingIdentity; all existing conditions remain unchanged.hasSemanticChunks()=true→hasOnlyFtsChunks=false→canRebuildMissingIdentitystill requires the provider. This prevents silent downgrade of semantic vector indexes.AI Assistance 🤖