OpenClaw memory search

Vector + FTS index over agent memory files — the subsystem behind memory_search, managed by the openclaw memory CLI.

Goals

Semantic recall over MEMORY.md and memory/*.md across six agents. Dogfooded 2026-07-29 by way of a failure: every session for days had opened with "memory search is currently paused (index built with a different embedding config)," and Gavin finally asked why the note kept recurring.

Effectiveness

Adequate. Revised 2026-07-30: worse than adequate — the advertised fix is the disease. The day after the post-mortem below, the index was "broken" again. It turns out the rebuild command the error message tells you to run is precisely what re-breaks the running gateway. See "Day two," below.

What the issue actually was

Two issues, one technical and one procedural:

  1. Stale index, missing identity metadata. The main agent's index predated the current embedding config, and memory status reported Index identity: index metadata is missing. OpenClaw responds by pausing vector search until a rebuild — safe, but silent about degradation since FTS keeps working.
  2. The nag loop had no actor. The housekeeping note re-surfaced at the start of every session with the fix command included, the agent relayed it to Gavin daily, and nobody — human or agent — ever ran the command. The note was re-reporting the same unfixed state, not a fresh breakage. Fixed in one command the moment someone actually executed it.

What made it effective

Friction / pain points / surprises

A daily nag that never self-heals. The rebuild is safe and idempotent — the system could just run it. Instead it emitted the same note every session indefinitely. A warning with a known-safe remedy and no auto-repair (or at least no escalation after N repeats) trains everyone to read it as weather.

Two different fix commands for the same fault. The housekeeping note says openclaw memory index --force; memory status says openclaw memory status --index --agent main. Both work, but a rebuild spelled as a flag on a status command is a surprise, and the disagreement invites hesitation — plausibly part of why the fix sat unrun.

Two different diagnoses too. The note blames "a different embedding config"; status says "index metadata is missing." Same fault, two stories.

The real lesson is agent-procedural. Any housekeeping note carrying a concrete safe command should be executed in-session, not narrated. This is now standing policy in agent memory. (2026-07-30: policy retracted for this particular command — see below. Running it turned out to be what broke it.)

Day two: the fix was the disease (2026-07-30)

One day after the rebuild "fixed" everything, memory_search was paused again with the same "index metadata is missing." This time the CLI and the gateway were interrogated side by side, and they disagreed:

The tiebreaker was /proc/<gateway-pid>/fd: the gateway held open descriptors to main.sqlite.backup-fa714fb2-… (deleted). It was reading a database that no longer exists.

The mechanism, from manager-*.js in the dist: a full reindex is atomic-by-rename. swapMemoryIndexFiles moves the live main.sqlite to main.sqlite.backup-<uuid>, promotes the freshly built temp DB into place, then deletes the backup. Atomic and correct — for the disk. Any long-lived process with an open SQLite handle (the gateway) keeps the old inode across the rename, so after the swap it is permanently reading the pre-rebuild file, which is now deleted. SQLite never errors; the file is simply frozen with whatever state earned the rebuild in the first place. The gateway's cached index manager re-checks identity against that ghost and reports "metadata missing" until the process restarts.

So the actual daily loop was:

  1. Gateway reports memory search paused, and its warning text prescribes openclaw memory index --force.
  2. An agent dutifully runs it — out-of-process, per yesterday's freshly minted policy.
  3. The swap orphans the gateway's handle. Disk: healthy. Gateway: broken — by the fix.
  4. Next session, same warning. GOTO 1.

Yesterday's post-mortem ("a fix command nobody ran") was exactly backwards: running the command is what re-breaks it, whenever the gateway is alive — which is always. A remedy embedded in an error message that reinfects the patient on execution is a genuinely nasty failure shape, because every observation ("CLI says healthy") reinforces the wrong conclusion ("the agent must not have run it").

Also falsified from yesterday: "FTS kept answering the whole time." In this failure mode the tool returns disabled: true with zero results — recall goes fully dark, not gracefully degraded.

Two smaller warts observed in passing: status --index printed Memory index failed: no such table: chunks_vec for four sub-agent stores (68 KB stubs whose sqlite-vec virtual table never got created) and then concluded "Memory index complete"; and the exit code stayed 0 throughout.

Correct remedies: restart the gateway after any out-of-process rebuild (it reopens the file), or better, never run CLI full rebuilds against a live gateway. Upstream, the index manager should re-open (or at least re-stat) the DB when its identity check fails — it holds all the evidence needed to notice the inode changed under it.