{"record":{"id":"9077b44008c5a51f","repo":"chroma-core/chroma","slug":"conditional-transaction-has-no-collection-scope","errorCode":null,"errorMessage":"conditional transaction has no collection scope","messagePattern":"conditional transaction has no collection scope","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"chromadb/api/conditional_http.py","lineNumber":233,"sourceCode":"        self._closed = True\n\n    def _ensure_open(self) -> None:\n        if self._closed:\n            raise ValueError(\"conditional transaction is closed\")\n\n    def _record_scope(\n        self, collection_id: UUID, tenant: str, database: str\n    ) -> ConditionalHttpScope:\n        scope = ConditionalHttpScope(str(collection_id), tenant, database)\n        if self._scope is None:\n            self._scope = scope\n        elif self._scope != scope:\n            raise ValueError(\"conditional transaction cannot span collections\")\n        return scope\n\n    def _require_scope(self) -> ConditionalHttpScope:\n        if self._scope is None:\n            raise ValueError(\"conditional transaction has no collection scope\")\n        return self._scope\n\n    def _validate_get_request(self, request_payload: ConditionalHttpGetPayload) -> None:\n        ids = request_payload.get(\"ids\")\n        if ids is not None:\n            for id in ids:\n                if id in self._buffered_write_ids:\n                    raise _invalid_read_after_write(id)\n            return\n\n        limit = request_payload.get(\"limit\")\n        if not isinstance(limit, int) or limit <= 0:\n            raise InvalidArgumentError(\n                \"transactional filter reads require a positive limit\"\n            )\n\n    def _validate_read_token(\n        self, expected_read_token: Optional[int], actual_read_token: Optional[int]","sourceCodeStart":215,"sourceCodeEnd":251,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/chromadb/api/conditional_http.py#L215-L251","documentation":"Raised by ConditionalHttpTransaction._require_scope (chromadb/api/conditional_http.py:233) when a scope-requiring step (recording a get result or building the commit payload) runs on a transaction that never bound itself to a collection. The scope is a frozen (collection_id, tenant, database) tuple recorded on the first prepare_get/buffer_* call; every later step must reuse it. In practice this is a defensive invariant: a transaction with buffered operations always has a scope, so hitting it usually means the transaction lifecycle was manipulated directly or a client bug is present.","triggerScenarios":"Calling prepare_commit_payload() or record_get() on a ConditionalHttpTransaction instance that never received a prepare_get/buffer_add/buffer_update/buffer_upsert/buffer_delete call for any collection. Not reachable through the normal client flow where the first read/write precedes the commit.","commonSituations":"Test code or custom wrappers that construct ConditionalHttpTransaction() manually and commit without operations; reusing or resetting transaction internals; a chromadb version mismatch between the client wrapper and this module after a partial upgrade.","solutions":["Start every transaction with at least one scoped operation (a get or a buffered write) before requesting the commit payload","Do not construct or drive ConditionalHttpTransaction yourself; obtain it from the HTTP client's transaction API so scope is always recorded first","If you only reached this via the public client API, upgrade chromadb to a consistent version and report the reproduction"],"exampleFix":"# before\ntxn = ConditionalHttpTransaction()\npayload = txn.prepare_commit_payload()  # no operation ever recorded\n# after\ncollection.get(ids=[\"doc1\"])            # first read records the scope\ncollection.add(ids=[\"doc2\"], embeddings=[emb])\ntxn.prepare_commit_payload()             # scope now present","handlingStrategy":"validation","validationCode":"from chromadb.api.conditional_http import ConditionalHttpTransaction\n\ndef safe_commit(txn):\n    # a transaction with any buffered operation always has a scope;\n    # commit only after at least one read/write against a collection\n    if not txn._operations:  # nothing buffered: commit is a no-op\n        txn.close()\n        return None\n    return txn.prepare_commit_payload()","typeGuard":null,"tryCatchPattern":"try:\n    txn.prepare_commit_payload()\nexcept ValueError as e:\n    if \"no collection scope\" in str(e):\n        # transaction was misused: restart it and perform a read/write first\n        ...\n    raise","preventionTips":["Always perform the first read or write before requesting a commit","Never construct ConditionalHttpTransaction yourself; use the client's transaction API","Treat this error as an invariant violation: log the transaction lifecycle that led to it"],"tags":["chromadb","transaction","http-client","internal-invariant","commit"],"backgroundTag":"transaction-validation-failed","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}