docs(investigate): root-cause metadata reshape bug (#32) - #140
Merged
Conversation
The chokepoint sanitizer in `ChromaCollection._sanitize_metadatas_for_chromadb` returns a new outer list but **aliases** each non-empty caller dict by reference. The comprehension `[m if (...) else sentinel for m in metadatas]` returns `m` itself whenever the guard passes, so the sanitized list still points at the caller's dicts. Any in-place mutation that empties one of those dicts between sanitize and chromadb's `validate_metadata` slips through unchecked — which is exactly the failure mode reported at ~120K/151K rebuilds. The same aliasing pattern appears in `repair.py:_extract_drawers` and `repair.py:_rebuild_one_collection`. Reproduction: tests/test_metadata_reshape_bug.py — 9 tests, including the regression demonstrating that post-sanitize mutation triggers the exact `ValueError: Expected metadata to be a non-empty dict in add` reported in #32. Proposed minimal fix (separate PR, recorded in the investigation doc): change `m` → `dict(m)` in three places. ~0.15s amortized cost on a 151K-drawer rebuild; eliminates the failure mode regardless of which specific mutator triggered it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
This was referenced May 23, 2026
jphein
added a commit
that referenced
this pull request
May 23, 2026
PR #140 (metadata reshape investigation) added 9 reproduction tests. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
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.
Fixes investigation portion of #32.
TL;DR
The chokepoint sanitizer
ChromaCollection._sanitize_metadatas_for_chromadb(
mempalace/backends/chroma.py:1094-1111) returns a new outer list butaliases each non-empty caller dict by reference. The
comprehension
[m if (...) else sentinel for m in metadatas]returns
mitself whenever the guard passes, so the sanitized liststill points at the caller's dict. Any in-place mutation between
sanitize and chromadb's
validate_metadataempties the dict fromunder the validator's feet — which matches the failure mode reported
at ~120K of a 151K rebuild.
The same aliasing pattern appears in
repair.py:_extract_drawersandrepair.py:_rebuild_one_collection.What this PR contains
docs/investigations/metadata-reshape-root-cause.md— fullwalkthrough of the chromadb internal flow (
Collection.add→normalize_insert_record_set→validate_metadatas→validate_metadata), confirmation that chromadb does not reshapemetadata in our code path, the aliasing diagnosis with empirical
reproduction, candidate mutators at scale, and a minimal-fix
proposal.
tests/test_metadata_reshape_bug.py— 9 tests. Two of them(
test_sanitizer_inner_dicts_alias_caller_dictsandtest_sanitized_dict_empty_after_caller_clear_passes_into_chromadb)reproduce the failure deterministically on a small palace, against
the real chromadb 1.5.9 validator. The remaining 7 establish the
upstream contract (
{}rejected,Noneaccepted) and verify thecurrent sanitizer behaviour the fix must preserve.
This is investigation-only. The actual fix (change
m→dict(m)in three places) is recorded in the investigation doc and proposed as
a follow-up PR so review can focus on the diagnosis first.
Verification
Architectural follow-ups
Recorded at the end of the investigation doc:
redundant repair-layer sanitizers and keep the chokepoint as the
sole owner of the "no empty dict reaches chromadb" contract — the
chokepoint is the structurally-correct layer for this invariant.
🤖 Generated with Claude Code