Skip to content

mempalace-issue-fts5-autoheal #2240

Description

@darkrhodes7

FTS5 auto-heal is unreachable when the serving interpreter's SQLite cannot detect the fault

Version: 3.7.0 (also present in 3.5.0)
Platform: Windows 11, uv tool install mempalace
Backend: chroma, default minilm embedder
Palace: 322,848 drawers, chroma.sqlite3 ~1.95 GB, 325,451 embedding_fulltext_search_content rows

Summary

repair.maybe_autoheal_fts5_index only runs when repair.sqlite_integrity_errors
returns a non-empty list. That function asks the SQLite bundled with whichever
interpreter is running MemPalace. On this install that is SQLite 3.47.1, and
3.47.1 does not detect the FTS5 fault present in this palace, while SQLite
3.50.4
reports it on the same file through the same mode=ro URI.

The result is that a palace can carry a malformed FTS5 inverted index
indefinitely while every MemPalace-run check — the startup integrity probe,
repair's preflight, and the post-mine validation in
palace._validate_palace_fts5_after_mine — reports it clean. The auto-heal
added for exactly this condition can never fire.

Separately, and more surprising: a 'rebuild' issued by 3.47.1 does not clear
the fault
, while the same statement issued by 3.50.4 does. So even if
detection were fixed, the heal would still no-op on this interpreter.

Evidence

Same file, same URI, both pragma forms, minutes apart:

SQLite PRAGMA quick_check(1) PRAGMA quick_check
3.50.4 (CPython 3.14.6) malformed inverted index for FTS5 table main.embedding_fulltext_search same
3.47.1 (CPython 3.12.9 — MemPalace's uv venv) ok ok

FTS5's own command, from 3.50.4:

INSERT INTO embedding_fulltext_search(embedding_fulltext_search) VALUES('integrity-check');
-- DatabaseError: database disk image is malformed

Table definition:

CREATE VIRTUAL TABLE embedding_fulltext_search USING fts5(string_value, tokenize='trigram')
-- embedding_fulltext_search_config: version = 4

Rebuild behaviour, on a byte copy of the live database so the result is
attributable to the rebuild alone:

'rebuild' issued by duration 3.50.4 quick_check afterwards
3.47.1 76s still malformed
3.50.4 85s clean, and integrity-check passes

After rebuilding the live palace with 3.50.4 under mine_palace_lock,
quick_check is clean, integrity-check passes, embedding_fulltext_search_content
is unchanged at 325,451 rows, and MemPalace (still on 3.47.1) returns identical
search results with identical BM25 scores — so a 3.50.4-written index reads back
correctly on 3.47.1 at this scale.

What I could not reproduce

A synthetic fts5(body, tokenize='trigram') table of 4,000 rows round-trips
clean in both directions between 3.47.1 and 3.50.4, with and without an
explicit 'rebuild'. So this is not simply "3.50.4 rejects any trigram index
3.47.1 writes" — something about this palace's data or scale is required, and I
do not have a minimal reproduction. Candidates I did not chase: multi-segment
indexes at ~2 GB, and content predating the embedded-NUL sanitisation in the
3.6.0 line (#1716, #1872, #1878, #1927, #1928).

I am reporting it without that minimal case because the detection asymmetry
stands on its own and is straightforward to check.

Impact

Nothing is visibly broken while MemPalace stays on SQLite < 3.50: BM25 search
returns sensible scores, because the index is self-consistent for the build that
wrote it. The problem is latent and surfaces on interpreter upgrade — at which
point quick_check starts failing, repair aborts at the preflight with
"ABORT: SQLite-layer corruption detected before repair rebuild", and the operator
is told their palace is corrupt. On this palace the underlying content was intact
the whole time.

It also means the several auto-heal paths added in the 3.6.0/3.7.0 line are
silently inert on any install whose Python bundles an older SQLite, which
includes the documented uv tool install route.

Suggestions

  1. Log the SQLite version alongside integrity verdicts. A "clean" from a
    build that cannot see this class of fault is not the same claim as a clean
    from one that can, and today they are indistinguishable in the output.
  2. Consider a floor for the integrity check, or a note in
    docs/write-routing-policy.md that integrity results are only as good as the
    linked SQLite.
  3. Worth confirming independently that FTS5 'rebuild' on 3.47.1 can leave a
    large trigram index in a state 3.50.4 rejects. If that holds generally it
    affects the heal path regardless of detection.

Happy to run further diagnostics against this palace — it reproduces reliably
here and I have a verified pre-change backup.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions