{"record":{"id":"a826d8214fabec28","repo":"chroma-core/chroma","slug":"transactional-update-for-id-id-requires-a-prio","errorCode":null,"errorMessage":"transactional update for id \"{id}\" requires a prior read proving the id is present","messagePattern":"transactional update for id \"(.+?)\" requires a prior read proving the id is present","errorType":"exception","errorClass":"InvalidArgumentError","httpStatus":400,"severity":"error","filePath":"chromadb/api/conditional_http.py","lineNumber":304,"sourceCode":"            if id in call_ids:\n                raise InvalidArgumentError(\n                    f'transactional write request contains duplicate id \"{id}\"'\n                )\n            call_ids.add(id)\n            if id in self._buffered_write_ids:\n                raise InvalidArgumentError(\n                    f'transaction already has a buffered write for id \"{id}\"'\n                )\n            self._validate_write_precondition(operation, id)\n\n    def _validate_write_precondition(self, operation: str, id: str) -> None:\n        if operation == \"add\" and id not in self._known_absent:\n            raise InvalidArgumentError(\n                f'transactional add for id \"{id}\" requires a prior read '\n                \"proving the id is absent\"\n            )\n        if operation == \"update\" and id not in self._known_present:\n            raise InvalidArgumentError(\n                f'transactional update for id \"{id}\" requires a prior read '\n                \"proving the id is present\"\n            )\n        if operation == \"delete\" and id not in self._known_present:\n            raise InvalidArgumentError(\n                f'transactional delete for id \"{id}\" requires a prior read '\n                \"proving the id is present\"\n            )\n\n\ndef require_conditional_http_transaction(\n    transaction: object,\n) -> ConditionalHttpTransaction:\n    if not isinstance(transaction, ConditionalHttpTransaction):\n        raise ValueError(\"invalid conditional transaction for HTTP client\")\n    return transaction\n\n","sourceCodeStart":286,"sourceCodeEnd":322,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/chromadb/api/conditional_http.py#L286-L322","documentation":"Raised by _validate_write_precondition (chromadb/api/conditional_http.py:304) when a transactional update targets an id the transaction has not proven present. _known_present only contains ids actually returned by a prior get inside the same transaction, so updating a record you have not read (or that came back empty) is rejected before anything is buffered. This keeps the OCC commit anchored to a read set that genuinely observed the current state of every updated id.","triggerScenarios":"collection.update(ids=[\"doc1\"], ...) inside a transaction without a preceding get that returned \"doc1\"; reading with where filters that returned other ids; reading the id in a different transaction; the get returning nothing because the id does not exist (then update is the wrong operation - the record is absent).","commonSituations":"Fire-and-forget update flows moved into a transaction without adding the read step; assuming a record exists from application state rather than from a transactional read; batch updates generated from an external source (e.g. a queue) without verifying presence first.","solutions":["Do an id-based get in the same transaction first and update only the ids it returned","Use upsert when the record may or may not exist","If the id was absent, add it after the read proves absence instead of updating"],"exampleFix":"# before\nwith client.transaction():\n    collection.update(ids=[\"doc1\"], metadatas=[{\"v\": 2}])\n# after\nwith client.transaction():\n    got = collection.get(ids=[\"doc1\"])\n    if got[\"ids\"]:\n        collection.update(ids=[\"doc1\"], metadatas=[{\"v\": 2}])\n    else:\n        collection.add(ids=[\"doc1\"], embeddings=[emb])","handlingStrategy":"validation","validationCode":"def txn_update(collection, ids, **fields):\n    got = collection.get(ids=ids)          # proves presence inside the txn\n    present = list(got[\"ids\"])\n    if present:\n        collection.update(ids=present, **fields)\n    return present","typeGuard":null,"tryCatchPattern":"from chromadb.errors import InvalidArgumentError\n\ntry:\n    collection.update(ids=ids, metadatas=metas)\nexcept InvalidArgumentError as e:\n    if \"requires a prior read\" in str(e):\n        got = collection.get(ids=ids)\n        collection.update(ids=list(got[\"ids\"]), metadatas=metas)\n    else:\n        raise","preventionTips":["Update only ids returned by an id-based get in the same transaction","Use upsert when presence is uncertain","Do not derive existence from application state; derive it from the transactional read"],"tags":["chromadb","transaction","update","read-before-write","occ"],"backgroundTag":"transaction-validation-failed","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}