{"record":{"id":"a68daf7858b2c520","repo":"MemPalace/mempalace","slug":"collection-was-built-with-embedder-stored-model-n","errorCode":null,"errorMessage":"collection was built with embedder {stored.model_name!r} but the current embedder is {current.model_name!r}. Searching across a model swap silently degrades recall. Re-embed the palace, or run `mempalace palace set-embedder --model <name> --force` to record the new identity if you know the vectors are compatible.","messagePattern":"collection was built with embedder (.+?) but the current embedder is (.+?)\\. Searching across a model swap silently degrades recall\\. Re-embed the palace, or run `mempalace palace set-embedder --model <name> --force` to record the new identity if you know the vectors are compatible\\.","errorType":"exception","errorClass":"EmbedderIdentityMismatchError","httpStatus":null,"severity":"critical","filePath":"mempalace/backends/base.py","lineNumber":239,"sourceCode":"    dim_conflict = bool(stored.dimension and current.dimension) and (\n        stored.dimension != current.dimension\n    )\n    name_conflict = stored.model_name != current.model_name\n\n    if not dim_conflict and not name_conflict:\n        return \"known_match\"\n\n    if force_model_swap:\n        return \"known_mismatch\"\n\n    if dim_conflict:\n        raise DimensionMismatchError(\n            f\"collection was built with a {stored.dimension}-dim embedder \"\n            f\"({stored.model_name!r}) but the current embedder is \"\n            f\"{current.dimension}-dim ({current.model_name!r}); the stored \"\n            \"vectors are incompatible. Re-embed the palace to switch models.\"\n        )\n    raise EmbedderIdentityMismatchError(\n        f\"collection was built with embedder {stored.model_name!r} but the \"\n        f\"current embedder is {current.model_name!r}. Searching across a model \"\n        \"swap silently degrades recall. Re-embed the palace, or run \"\n        \"`mempalace palace set-embedder --model <name> --force` to record the \"\n        \"new identity if you know the vectors are compatible.\"\n    )\n\n\n@dataclass(frozen=True)\nclass HealthStatus:\n    ok: bool\n    detail: str = \"\"\n\n    @classmethod\n    def healthy(cls, detail: str = \"\") -> \"HealthStatus\":\n        return cls(ok=True, detail=detail)\n\n    @classmethod","sourceCodeStart":221,"sourceCodeEnd":257,"githubUrl":"https://github.com/MemPalace/mempalace/blob/06cb6987f02610784fefbad4b2bd5d026d164ba6/mempalace/backends/base.py#L221-L257","documentation":"Raised as TruncationDetected when the on-disk chroma.sqlite3 reports more embedding rows than the chromadb collection layer returned during extraction. This means the segment metadata is stale (classically after a manual HNSW quarantine), and proceeding with a rebuild would silently destroy the difference. It is a load-bearing safety abort that protects against data loss.","triggerScenarios":"check_extraction_safety() in repair.py finds sqlite_count > extracted without confirm_truncation_ok=True. Typical after an HNSW index quarantine or a chromadb version change that leaves segment metadata inconsistent with the embeddings table.","commonSituations":"Running `mempalace rebuild` / repair after manually moving HNSW segment files, after a chromadb upgrade with segment drift, or against a palace that was copied while chromadb held it open.","solutions":["Restore the palace from your most recent backup, then re-mine — safest path.","Direct-extract rows from chroma.sqlite3 (extract_via_sqlite exists for this) and rebuild the palace from the recovered rows plus source files.","Only if you have independently verified (direct sqlite3 COUNT query) that the palace truly contains only {extracted} drawers, re-run with --confirm-truncation-ok."],"exampleFix":"# before\nrepair.check_extraction_safety(palace_path, extracted=5, sqlite_count=10_000)\n# raises TruncationDetected\n\n# after (only after verifying via sqlite3)\nrepair.check_extraction_safety(palace_path, extracted=5, sqlite_count=5,\n    confirm_truncation_ok=True)","handlingStrategy":"validation","validationCode":"from mempalace import repair\ncount = repair.sqlite_drawer_count(palace_path)\nif count is not None:\n    extracted = len(list(repair.extract_via_sqlite(palace_path, 'drawers')))\n    assert extracted >= count or independently_verified, 'truncation risk'","typeGuard":null,"tryCatchPattern":"try:\n    repair.check_extraction_safety(palace_path, extracted, confirm_truncation_ok=False)\nexcept repair.TruncationDetected as e:\n    # e carries sqlite_count and extracted — decide recovery vs confirm\n    if verified_true_count(e.extracted):\n        rerun_with_confirm()\n    else:\n        restore_from_backup_or_sqlite_extract()","preventionTips":["Never manually quarantine/move HNSW segment files while chromadb is running.","Keep regular palace backups before repairs.","Run repairs with no other process holding the palace open so the SQLite cross-check works."],"tags":["data-loss","chromadb","sqlite","truncation","safety-guard"],"backgroundTag":null,"analyzedSha":"06cb6987f02610784fefbad4b2bd5d026d164ba6","analyzedAt":"2026-08-15T03:03:36.213Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}