{"record":{"id":"40269902c5269652","repo":"MemPalace/mempalace","slug":"backend-does-not-support-facet-counts","errorCode":null,"errorMessage":"backend does not support facet_counts","messagePattern":"backend does not support facet_counts","errorType":"exception","errorClass":"UnsupportedCapabilityError","httpStatus":null,"severity":"critical","filePath":"mempalace/backends/base.py","lineNumber":514,"sourceCode":"                kwargs[\"where\"] = where\n            batch = self.get(**kwargs)\n            batch_meta = batch.metadatas if hasattr(batch, \"metadatas\") else batch.get(\"metadatas\")\n            if not batch_meta:\n                break\n            all_meta.extend(batch_meta)\n            if len(batch_meta) < page_size:\n                break\n            offset += len(batch_meta)\n        return all_meta\n\n    def facet_counts(\n        self,\n        field: str,\n        where: Optional[dict] = None,\n        limit: int = 1000,\n    ) -> dict[str, int]:\n        \"\"\"Return counts for each distinct value of a metadata field.\"\"\"\n        raise UnsupportedCapabilityError(\"backend does not support facet_counts\")\n\n    def maintenance_state(self) -> dict:\n        \"\"\"Return a structured snapshot of this collection's maintenance state.\n\n        Free-form per backend (e.g. row count, whether a vector index exists,\n        last-analyze age). Used by benchmark harnesses to record state\n        alongside each latency/recall measurement so an un-analyzed store is\n        not compared against a settled one (RFC 001). Defaults to empty.\n        \"\"\"\n        return {}\n\n    def run_maintenance(self, kind: str) -> \"MaintenanceResult\":\n        \"\"\"Run a maintenance ``kind`` and return an observable result (RFC 001).\n\n        Backends advertise supported kinds in ``BaseBackend.maintenance_kinds``\n        and override this. The default supports nothing, so every kind raises\n        :class:`UnsupportedMaintenanceKindError`. Implementations MUST serialize\n        concurrent same-kind runs and report ``already_running`` rather than","sourceCodeStart":496,"sourceCodeEnd":532,"githubUrl":"https://github.com/MemPalace/mempalace/blob/06cb6987f02610784fefbad4b2bd5d026d164ba6/mempalace/backends/base.py#L496-L532","documentation":"Raised as TruncationDetected when extraction returned exactly CHROMADB_DEFAULT_GET_LIMIT drawers and the on-disk SQLite count could not be cross-checked (sqlite_count is None, e.g. schema unreadable or DB locked). Because that exact count matches chromadb's internal default get() limit, the code cannot distinguish a genuine palace size from silent capping, so it refuses to overwrite the palace.","triggerScenarios":"check_extraction_safety() sees cap_signal=True and sqlite_count=None. Happens when the palace holds exactly chromadb's default get limit rows, or extraction was capped, while sqlite_drawer_count() returned None due to version drift, missing tables, or a locked chroma.sqlite3.","commonSituations":"Large palaces hitting chromadb's default get() limit during repair; chromadb schema changes across upgrades making sqlite_drawer_count return None; running repair while another process holds the SQLite file.","solutions":["Close any process holding chroma.sqlite3 (miners, MCP server) and retry so the SQLite cross-check can run.","Independently verify the true count: query `SELECT COUNT(*) FROM embeddings` directly with the sqlite3 CLI.","If the palace genuinely holds exactly that many drawers, re-run with --confirm-truncation-ok.","If it holds more, use the direct-extract path to recover the capped rows instead of confirming."],"exampleFix":"# Verify true count directly before confirming\n# sqlite3 <palace>/chroma.sqlite3 'SELECT COUNT(*) FROM embeddings;'\n\n# after verification\nrepair.rebuild_index(palace_path, confirm_truncation_ok=True)","handlingStrategy":"validation","validationCode":"from mempalace import repair\ncount = repair.sqlite_drawer_count(palace_path)\nif count is None:\n    raise SystemExit('cannot cross-check; close processes holding chroma.sqlite3')","typeGuard":null,"tryCatchPattern":"try:\n    repair.check_extraction_safety(palace_path, extracted)\nexcept repair.TruncationDetected as e:\n    if e.sqlite_count is None:  # cap-signal variant\n        true_count = direct_sqlite_count(palace_path)\n        if true_count == extracted:\n            rerun_with_confirm_truncation_ok()","preventionTips":["Close miners/MCP servers before repair so sqlite_drawer_count succeeds instead of returning None.","Independently verify counts with the sqlite3 CLI before passing --confirm-truncation-ok.","Watch for extraction counts landing exactly on chromadb's default get limit — a truncation smell."],"tags":["data-loss","chromadb","truncation","sqlite","safety-guard"],"backgroundTag":null,"analyzedSha":"06cb6987f02610784fefbad4b2bd5d026d164ba6","analyzedAt":"2026-08-15T03:03:36.213Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}