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:
- Stale index, missing identity metadata. The
mainagent's index predated the current embedding config, andmemory statusreportedIndex identity: index metadata is missing. OpenClaw responds by pausing vector search until a rebuild — safe, but silent about degradation since FTS keeps working. - 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
openclaw memory index --forcerebuilt every agent's index (main, coding, artemis, throuple, researcher, qwen-coder) in one shot, seconds, exit 0.openclaw memory statusis a genuinely good diagnostic screen: per-agent provider, files/chunks indexed, dirty flag, vector dims, FTS state, cache stats.- Graceful degradation: FTS stayed live while vectors were paused, so recall never went fully dark.
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:
openclaw memory status --index— all green. 11/11 files, 20 chunks, meta row present (nomic-embed-text, ollama, 768 dims, matching providerKey), vector store ready, FTS ready.- The gateway's
memory_searchtool — "index metadata is missing," before and after two freshmemory index --forceruns.
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:
- Gateway reports memory search paused, and its warning text prescribes
openclaw memory index --force. - An agent dutifully runs it — out-of-process, per yesterday's freshly minted policy.
- The swap orphans the gateway's handle. Disk: healthy. Gateway: broken — by the fix.
- 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.