Repository navigation
fix(embed): a corrupt embedding store no longer kills the MCP server (2.145.2) - #170
Merged
Merged
Conversation
drknowhow
force-pushed
the
fix/embedding-store-corruption-kills-mcp
branch
from
September 10, 2026 12:41
89d40e3 to
6b47ef9
Compare
Contributor
|
Rebased onto The only conflicts were version bookkeeping —
No change to New head |
…(2.145.2) A damaged HNSW segment opens cleanly and faults on the first real read, which lands on the background c3-embed-index thread as a native access violation and kills the MCP server with no error. _open_chroma now probes with count() on the init path, quarantines a store that fails, and reopens empty (one retry); the hash file moves with it so the rebuild happens. Rebased from #170 onto 2.145.1; only version files and CHANGELOG conflicted. Verified: the 5 new tests fail on main's embedding_index.py and pass here. Claude-Session: https://claude.ai/code/session_01DEKoSbeEFepPGwPHQDHxAJ
drknowhow
force-pushed
the
fix/embedding-store-corruption-kills-mcp
branch
from
September 22, 2026 12:57
6b47ef9 to
97b4dfd
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What broke
c3showed up as failed to connect with no error anywhere. Claude Code's own log for the session was the whole story:It connected, listed its tools, and died. Reproduced 3/3 by hand; under
-X faulthandlerthe process exits 139 with:Root cause
The HNSW segment in
.c3/embeddings/chromadbwas corrupt. A damaged segment does not surface when the store is opened — chromadb hands back a client and a collection handle without complaint, then faults on the first real read. Driving that read directly gives the catchable form:But in the server that read happens on the background
c3-embed-indexthread, where it comes back as a native access violation instead, and takes the process with it. Nothing is logged, and the host can only report a dead server.Second occurrence —
.c3/embeddings/file_hashes_corrupt_20260717.jsonis the 2026-07-17 one, cleaned up by hand at the time._init_backendshad no corruption handling at all, so the store's failure mode was always "kill the server".The fix
_open_chromaprobes the store withcount()on the init path, where the failure is still a catchable Python exception rather than a fault on a background thread. A failed probe is treated as corruption:chroma.sqlite3otherwise — a rename fails withPermissionErrorwhile it is open, and succeeds afterclient.close()(verified; the globalclear_system_cache()is deliberately not used, since other stores share the process)..c3/embeddings/quarantine_corrupt_<timestamp>/, damaged bytes preserved for post-mortem rather than deleted._init_backendsdegrades the index to unavailable instead of taking the server down.The store is a rebuildable cache, so recovery costs one re-embed and no data.
Verification
get()that used to segfault returns cleanly. Exit 0.initialize+tools/list+ a secondtools/list45 s later, all answered, exit 0, empty stderr. Before: exit 139.tests/test_embedding_index_corrupt_store.pycovering healthy-store passthrough, quarantine-and-reopen, hash-file handling, retry bound, and degrade-not-raise.tests/test_ci_act.py::TestActIntegration::test_runs_a_linux_job_and_reports_a_clean_failure('unparsed' != 'ruff') andtests/test_codex_mcp_transport.py::test_stdio_host_session(30 s handshake timeout — fails 2/2 at HEAD, 1/2 with this branch).Note
The corrupt store on this box has already been quarantined by hand, so
c3connects again today. This change is what keeps the next corruption from presenting as a silently dead MCP server.https://claude.ai/code/session_01TmRb4DvoqKCroerYKJ9poc