openclaw - 💡(How to fix) Fix runUnsafeReindex crashes with "no such table: chunks_vec" when sqlite-vec is enabled [1 participants]

Official PRs (…)
ON THIS PAGE

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#70559Fetched 2026-04-24 05:56:27
View on GitHub
Comments
0
Participants
1
Timeline
0
Reactions
0
Author
Participants

The OPENCLAW_TEST_FAST=1 + OPENCLAW_TEST_MEMORY_UNSAFE_REINDEX=1 code path (which triggers MemoryIndexManager.runUnsafeReindex instead of runSafeReindex) is unusable on any deployment where sqlite-vec is loaded and chunks_vec is populated — the full reindex crashes immediately after resetIndex() with:

SqliteError: no such table: chunks_vec

runSafeReindex builds a fresh temp db and atomic-swaps via fs.rename, which on Windows fails with EBUSY whenever any other live process holds an open handle to memory/<agent>.sqlite (e.g. long-running agent/gateway runtimes). The unsafe variant is the only in-place alternative in the code base — but it does not survive when vectors are enabled, which effectively leaves Windows users with no working force-reindex path when agents are live.

Error Message

SqliteError: no such table: chunks_vec

Root Cause

  1. MemoryIndexManager.resetIndex() (in extensions/memory-core/src/memory/manager-sync-ops.ts, compiled to dist/manager-*.js:1930) unconditionally calls this.dropVectorTable() and sets this.vector.dims = undefined. After this call, chunks_vec no longer exists.

  2. runUnsafeReindexsyncMemoryFiles (dist/manager-*.js:1457) prepares a DELETE statement referencing chunks_vec as part of its setup block, before the first chunk is written:

    const deleteVectorRowsByPathAndSource = this.vector.enabled && this.vector.available
      ? this.db.prepare(`DELETE FROM ${VECTOR_TABLE} WHERE id IN (SELECT id FROM chunks WHERE path = ? AND source = ?)`)
      : null;
  3. At this point this.vector.available is still true (it is only set to false on a load error, not on table drop) — so db.prepare() runs, and throws no such table: chunks_vec at parse time.

  4. The exception propagates out of runUnsafeReindex and the entire reindex aborts for that agent. In our deployment it reproducibly failed for all 6 agents (main, premium_Tt, premium_MMZ, asya, pnv, patent).

The runSafeReindex path does not hit this bug because it operates on a fresh temp DB, where ensureSchema() + lazy ensureVectorTable(dims) run from scratch.

Fix Action

Fix / Workaround

Happy to send a PR with the patch above if the approach is acceptable.

Code Example

SqliteError: no such table: chunks_vec

---

const deleteVectorRowsByPathAndSource = this.vector.enabled && this.vector.available
     ? this.db.prepare(`DELETE FROM ${VECTOR_TABLE} WHERE id IN (SELECT id FROM chunks WHERE path = ? AND source = ?)`)
     : null;

---

set OPENCLAW_TEST_FAST=1
   set OPENCLAW_TEST_MEMORY_UNSAFE_REINDEX=1
   openclaw memory index --force

---

resetIndex() {
   this.db.exec(`DELETE FROM files`);
   this.db.exec(`DELETE FROM chunks`);
   if (this.fts.enabled && this.fts.available) {
     try { this.db.exec(`DROP TABLE IF EXISTS ${FTS_TABLE}`); } catch {}
   }
   this.ensureSchema();
-  this.dropVectorTable();
-  this.vector.dims = undefined;
+  if (this.vector.enabled && this.vector.available) {
+    try {
+      this.db.exec(`DELETE FROM ${VECTOR_TABLE}`);
+    } catch {
+      this.dropVectorTable();
+      this.vector.dims = undefined;
+    }
+  } else {
+    this.dropVectorTable();
+    this.vector.dims = undefined;
+  }
   this.sessionsDirtyFiles.clear();
 }
RAW_BUFFERClick to expand / collapse

runUnsafeReindex crashes with "no such table: chunks_vec" when sqlite-vec is enabled

Environment

  • OpenClaw version: 2026.4.10 (commit 44e5b62)
  • Platform: Windows 11 Pro 10.0.26200
  • Node.js embedded SQLite: DatabaseSync (node:sqlite) with sqlite-vec extension
  • Memory backend: file-backed sqlite per agent (6 agents: main, premium_Tt, premium_MMZ, asya, pnv, patent)
  • Embedding provider: ollama / bge-m3 (1024 dims)
  • Vector table config: memory.store.vector.enabled = true

Summary

The OPENCLAW_TEST_FAST=1 + OPENCLAW_TEST_MEMORY_UNSAFE_REINDEX=1 code path (which triggers MemoryIndexManager.runUnsafeReindex instead of runSafeReindex) is unusable on any deployment where sqlite-vec is loaded and chunks_vec is populated — the full reindex crashes immediately after resetIndex() with:

SqliteError: no such table: chunks_vec

runSafeReindex builds a fresh temp db and atomic-swaps via fs.rename, which on Windows fails with EBUSY whenever any other live process holds an open handle to memory/<agent>.sqlite (e.g. long-running agent/gateway runtimes). The unsafe variant is the only in-place alternative in the code base — but it does not survive when vectors are enabled, which effectively leaves Windows users with no working force-reindex path when agents are live.

Root cause

  1. MemoryIndexManager.resetIndex() (in extensions/memory-core/src/memory/manager-sync-ops.ts, compiled to dist/manager-*.js:1930) unconditionally calls this.dropVectorTable() and sets this.vector.dims = undefined. After this call, chunks_vec no longer exists.

  2. runUnsafeReindexsyncMemoryFiles (dist/manager-*.js:1457) prepares a DELETE statement referencing chunks_vec as part of its setup block, before the first chunk is written:

    const deleteVectorRowsByPathAndSource = this.vector.enabled && this.vector.available
      ? this.db.prepare(`DELETE FROM ${VECTOR_TABLE} WHERE id IN (SELECT id FROM chunks WHERE path = ? AND source = ?)`)
      : null;
  3. At this point this.vector.available is still true (it is only set to false on a load error, not on table drop) — so db.prepare() runs, and throws no such table: chunks_vec at parse time.

  4. The exception propagates out of runUnsafeReindex and the entire reindex aborts for that agent. In our deployment it reproducibly failed for all 6 agents (main, premium_Tt, premium_MMZ, asya, pnv, patent).

The runSafeReindex path does not hit this bug because it operates on a fresh temp DB, where ensureSchema() + lazy ensureVectorTable(dims) run from scratch.

Reproduction

  1. Install OpenClaw 2026.4.10 on Windows with at least one agent using memory.store.vector.enabled = true and a working sqlite-vec extension path.
  2. Let the agent index some memory files so that chunks_vec is populated.
  3. Leave the agent runtime alive (so the sqlite handle is held — triggers the EBUSY on the safe path as well).
  4. Run:
    set OPENCLAW_TEST_FAST=1
    set OPENCLAW_TEST_MEMORY_UNSAFE_REINDEX=1
    openclaw memory index --force
  5. Expected: full in-place rebuild, each agent prints "Memory index updated (...)".
  6. Actual: every agent fails with Memory index failed (<agent>): no such table: chunks_vec.

Proposed fix

Keep chunks_vec schema-intact inside resetIndex(), purging rows instead of dropping the virtual table. The ensureVectorTable(dimensions) call later in the insert path (manager-*.js:1199) already handles the "dimensions changed" case by dropping and recreating, so nothing is lost.

Diff against extensions/memory-core/src/memory/manager-sync-ops.ts:

 resetIndex() {
   this.db.exec(`DELETE FROM files`);
   this.db.exec(`DELETE FROM chunks`);
   if (this.fts.enabled && this.fts.available) {
     try { this.db.exec(`DROP TABLE IF EXISTS ${FTS_TABLE}`); } catch {}
   }
   this.ensureSchema();
-  this.dropVectorTable();
-  this.vector.dims = undefined;
+  if (this.vector.enabled && this.vector.available) {
+    try {
+      this.db.exec(`DELETE FROM ${VECTOR_TABLE}`);
+    } catch {
+      this.dropVectorTable();
+      this.vector.dims = undefined;
+    }
+  } else {
+    this.dropVectorTable();
+    this.vector.dims = undefined;
+  }
   this.sessionsDirtyFiles.clear();
 }

Row-level DELETE FROM chunks_vec on a vec0 virtual table is supported and O(rows) — same order of magnitude as the DELETE FROM chunks already happening two lines above. If for any reason the DELETE fails (table does not exist yet on a brand-new DB), we fall back to the original drop+clear-dims behavior.

Broader note

The OPENCLAW_TEST_* naming of the gate suggests this path was introduced for test runs and was not intended for production. On Windows, however, it is effectively the only way to avoid EBUSY on the nightly memory index --force, because the safe path's fs.rename(<target>, <backup-uuid>) (dist/manager-*.js:923 swapMemoryIndexFiles) cannot succeed while any other process holds a handle on <agent>.sqlite. Closing your own connection does not help — other live processes still hold theirs, and on Windows those files were not opened with FILE_SHARE_DELETE.

It would be great if either:

  • runUnsafeReindex became supported with sqlite-vec (fix above makes it so), or
  • the safe-path swap learned a Windows-specific fallback (e.g. VACUUM INTO temp + sqlite backup API write-back through the live handle, avoiding rename entirely).

Happy to send a PR with the patch above if the approach is acceptable.

extent analysis

TL;DR

The proposed fix involves modifying the resetIndex() method to purge rows from the chunks_vec table instead of dropping it, ensuring the table remains schema-intact.

Guidance

  • Review the proposed diff against extensions/memory-core/src/memory/manager-sync-ops.ts to understand the suggested changes.
  • Verify that the DELETE FROM ${VECTOR_TABLE} statement is supported and efficient for your use case.
  • Consider the broader implications of using runUnsafeReindex in production, as it was initially intended for test runs.
  • Evaluate the potential benefits of implementing a Windows-specific fallback for the safe-path swap to avoid EBUSY issues.

Example

The proposed fix includes a code snippet that demonstrates the modified resetIndex() method:

resetIndex() {
  this.db.exec(`DELETE FROM files`);
  this.db.exec(`DELETE FROM chunks`);
  if (this.fts.enabled && this.fts.available) {
    try { this.db.exec(`DROP TABLE IF EXISTS ${FTS_TABLE}`); } catch {}
  }
  this.ensureSchema();
  if (this.vector.enabled && this.vector.available) {
    try {
      this.db.exec(`DELETE FROM ${VECTOR_TABLE}`);
    } catch {
      this.dropVectorTable();
      this.vector.dims = undefined;
    }
  } else {
    this.dropVectorTable();
    this.vector.dims = undefined;
  }
  this.sessionsDirtyFiles.clear();
}

Notes

The fix assumes that the DELETE FROM ${VECTOR_TABLE} statement is supported and efficient. However, if this is not the case, alternative approaches may be necessary.

Recommendation

Apply the proposed workaround by modifying the resetIndex() method to purge rows from the chunks_vec table instead of dropping it, as this approach ensures the table remains schema-intact and allows runUnsafeReindex to work with sqlite-vec.

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