openclaw - 💡(How to fix) Fix [Bug]: memory_search vector fallback can OOM gateway when sqlite-vec is unavailable on large memory DBs [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#74546Fetched 2026-04-30 06:23:11
View on GitHub
Comments
1
Participants
2
Timeline
2
Reactions
2
Timeline (top)
closed ×1commented ×1

When sqlite-vec is unavailable, searchVector() falls back to an in-memory cosine path that calls listChunks() and loads every indexed chunk/embedding into the Node heap; on a large memory DB this can exhaust V8 heap and crash the OpenClaw gateway.

Error Message

log.warn(

Root Cause

Affected: OpenClaw installations with large memory DBs where sqlite-vec/vector search is unavailable or degraded. Severity: Critical for affected installations because a normal memory_search can crash the gateway process. Frequency: Reproducible when vector readiness is false and chunk count is large enough to exceed available V8 heap. Consequence: Gateway OOM crash, memory search unavailable, agent workflows depending on memory may fail.

Code Example

Environment:
OpenClaw 2026.4.26 (be8c246)
Node.js v22.22.1
Ubuntu 24.04 / Linux 6.17.0-22-generic x86_64

Observed DB shape:
chunks:          12,553 rows
chunks text:     17,017,108 bytes total
chunks.embedding avg/max length: 2 / 2 bytes
embedding_cache: 11,483 rows
embedding_cache total embedding bytes: 598,910,162
embedding_cache avg/max embedding length: 52,156 / 52,391 bytes
chunks_vec_exists: 0

Relevant installed dist path observed in 2026.4.26:
~/.npm-global/lib/node_modules/openclaw/dist/manager-ICKYl3BU.js

Relevant fallback code shape:
async function searchVector(params) {
  if (params.queryVec.length === 0 || params.limit <= 0) return [];
  if (await params.ensureVectorReady(params.queryVec.length)) {
    // sqlite-vec fast path
  }

  return listChunks({
    db: params.db,
    providerModel: params.providerModel,
    sourceFilter: params.sourceFilterChunks
  }).map((chunk) => ({
    chunk,
    score: cosineSimilarity(params.queryVec, chunk.embedding)
  }))
  .filter(...)
  .toSorted(...)
  .slice(0, params.limit)
  .map(...);
}

function listChunks(params) {
  return params.db.prepare(`SELECT id, path, start_line, end_line, text, embedding, source
    FROM chunks
   WHERE model = ?${params.sourceFilter.sql}`)
    .all(params.providerModel, ...params.sourceFilter.params)
    .map((row) => ({
      ...,
      embedding: parseEmbedding(row.embedding),
      ...
    }));
}

---

const MAX_FALLBACK_CHUNKS = 2000;
const totalChunks = readCount(
  params.db.prepare(
    `SELECT COUNT(*) AS count FROM chunks WHERE model = ?${params.sourceFilterChunks.sql}`
  ).get(params.providerModel, ...params.sourceFilterChunks.params)
);
if (totalChunks > MAX_FALLBACK_CHUNKS) {
  log.warn(
    `[memory-search] sqlite-vec unavailable and chunk count (${totalChunks}) exceeds ` +
    `fallback limit (${MAX_FALLBACK_CHUNKS}). Skipping in-memory vector search to prevent OOM.`
  );
  return [];
}
RAW_BUFFERClick to expand / collapse

Bug type

Crash (process/app exits or hangs)

Beta release blocker

No

Summary

When sqlite-vec is unavailable, searchVector() falls back to an in-memory cosine path that calls listChunks() and loads every indexed chunk/embedding into the Node heap; on a large memory DB this can exhaust V8 heap and crash the OpenClaw gateway.

Steps to reproduce

  1. Use OpenClaw 2026.4.26 with memory search enabled and a memory DB large enough to contain thousands of indexed chunks.
  2. Ensure the vector table is unavailable/not created (chunks_vec absent), so ensureVectorReady() returns false.
  3. Trigger a memory search through the gateway/tool path.
  4. Observe searchVector() entering the fallback path and listChunks() selecting all chunks rows before JS cosine ranking.

Expected behavior

If sqlite-vec is unavailable, OpenClaw should not heap-load the full memory database. The vector fallback should be bounded/streamed, or should refuse large datasets and allow keyword/FTS search to carry results.

Actual behavior

On a real gateway memory DB, sqlite-vec/vector table was unavailable and the fallback path attempted an unbounded in-memory vector search over all chunks. The gateway hit the V8 heap ceiling and crashed with OOM.

OpenClaw version

2026.4.26 (be8c246)

Operating system

Ubuntu 24.04 / Linux 6.17.0-22-generic x86_64

Install method

npm global

Model

Memory embedding provider/model under test: NOT_ENOUGH_INFO

Provider / routing chain

OpenClaw gateway memory_search tool path. External chat/model routing is not required to reproduce the fallback OOM once the query embedding exists.

Additional provider/model setup details

The crash is in the local memory search fallback after query embedding generation. The relevant condition is ensureVectorReady() returning false / chunks_vec unavailable, not a specific chat provider.

Logs, screenshots, and evidence

Environment:
OpenClaw 2026.4.26 (be8c246)
Node.js v22.22.1
Ubuntu 24.04 / Linux 6.17.0-22-generic x86_64

Observed DB shape:
chunks:          12,553 rows
chunks text:     17,017,108 bytes total
chunks.embedding avg/max length: 2 / 2 bytes
embedding_cache: 11,483 rows
embedding_cache total embedding bytes: 598,910,162
embedding_cache avg/max embedding length: 52,156 / 52,391 bytes
chunks_vec_exists: 0

Relevant installed dist path observed in 2026.4.26:
~/.npm-global/lib/node_modules/openclaw/dist/manager-ICKYl3BU.js

Relevant fallback code shape:
async function searchVector(params) {
  if (params.queryVec.length === 0 || params.limit <= 0) return [];
  if (await params.ensureVectorReady(params.queryVec.length)) {
    // sqlite-vec fast path
  }

  return listChunks({
    db: params.db,
    providerModel: params.providerModel,
    sourceFilter: params.sourceFilterChunks
  }).map((chunk) => ({
    chunk,
    score: cosineSimilarity(params.queryVec, chunk.embedding)
  }))
  .filter(...)
  .toSorted(...)
  .slice(0, params.limit)
  .map(...);
}

function listChunks(params) {
  return params.db.prepare(`SELECT id, path, start_line, end_line, text, embedding, source
    FROM chunks
   WHERE model = ?${params.sourceFilter.sql}`)
    .all(params.providerModel, ...params.sourceFilter.params)
    .map((row) => ({
      ...,
      embedding: parseEmbedding(row.embedding),
      ...
    }));
}

Impact and severity

Affected: OpenClaw installations with large memory DBs where sqlite-vec/vector search is unavailable or degraded. Severity: Critical for affected installations because a normal memory_search can crash the gateway process. Frequency: Reproducible when vector readiness is false and chunk count is large enough to exceed available V8 heap. Consequence: Gateway OOM crash, memory search unavailable, agent workflows depending on memory may fail.

Additional information

This is related to prior sqlite-vec availability/performance issues, but the safety bug is distinct: even if sqlite-vec fails for any reason, the fallback path should be memory bounded.

Related issues found before filing:

  • #64776 sqlite-vec extension not loaded during gateway runtime memory sync (closed)
  • #65156 sqlite-vec loads but registers no functions / ABI mismatch (open)
  • #65244 missing allowExtension option in DatabaseSync (closed)
  • #66746 platform-specific sqlite-vec unavailable cases (open)
  • #69666 vector query KNN performance issue (closed)

Suggested low-risk fix: Before calling listChunks() in the fallback branch, count matching chunks and refuse the JS cosine fallback above a conservative threshold, for example 2,000 chunks:

const MAX_FALLBACK_CHUNKS = 2000;
const totalChunks = readCount(
  params.db.prepare(
    `SELECT COUNT(*) AS count FROM chunks WHERE model = ?${params.sourceFilterChunks.sql}`
  ).get(params.providerModel, ...params.sourceFilterChunks.params)
);
if (totalChunks > MAX_FALLBACK_CHUNKS) {
  log.warn(
    `[memory-search] sqlite-vec unavailable and chunk count (${totalChunks}) exceeds ` +
    `fallback limit (${MAX_FALLBACK_CHUNKS}). Skipping in-memory vector search to prevent OOM.`
  );
  return [];
}

Longer-term options:

  • Make sqlite-vec initialization more robust across runtime/platforms.
  • Replace the fallback with a streamed/batched bounded top-K algorithm.
  • Add a regression test that asserts vector fallback never loads an unbounded table into JS memory.

extent analysis

TL;DR

Implement a chunk count check before calling listChunks() in the fallback branch to prevent loading large datasets into memory.

Guidance

  • Identify the threshold for the maximum number of chunks to load into memory (e.g., 2000) to prevent OOM crashes.
  • Modify the searchVector() function to count the total number of chunks before calling listChunks() and refuse the fallback if the count exceeds the threshold.
  • Consider implementing a streamed or batched top-K algorithm to replace the current fallback approach.
  • Add a regression test to ensure the vector fallback never loads an unbounded table into JS memory.

Example

const MAX_FALLBACK_CHUNKS = 2000;
const totalChunks = readCount(
  params.db.prepare(
    `SELECT COUNT(*) AS count FROM chunks WHERE model = ?${params.sourceFilterChunks.sql}`
  ).get(params.providerModel, ...params.sourceFilterChunks.params)
);
if (totalChunks > MAX_FALLBACK_CHUNKS) {
  log.warn(
    `[memory-search] sqlite-vec unavailable and chunk count (${totalChunks}) exceeds ` +
    `fallback limit (${MAX_FALLBACK_CHUNKS}). Skipping in-memory vector search to prevent OOM.`
  );
  return [];
}

Notes

The suggested fix is a low-risk solution to prevent OOM crashes, but a more robust solution would involve making sqlite-vec initialization more reliable or replacing the fallback with a more efficient algorithm.

Recommendation

Apply the suggested low-risk fix to implement a chunk count check before calling listChunks() in the fallback branch, as it provides a simple and effective way to prevent OOM crashes.

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…

FAQ

Expected behavior

If sqlite-vec is unavailable, OpenClaw should not heap-load the full memory database. The vector fallback should be bounded/streamed, or should refuse large datasets and allow keyword/FTS search to carry results.

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 [Bug]: memory_search vector fallback can OOM gateway when sqlite-vec is unavailable on large memory DBs [1 comments, 2 participants]