chroma-core/chroma · error · InvalidArgumentError

transactional update for id "{id}" requires a prior read pro

Error message

transactional update for id "{id}" requires a prior read proving the id is present

What it means

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.

Source

Thrown at chromadb/api/conditional_http.py:304

            if id in call_ids:
                raise InvalidArgumentError(
                    f'transactional write request contains duplicate id "{id}"'
                )
            call_ids.add(id)
            if id in self._buffered_write_ids:
                raise InvalidArgumentError(
                    f'transaction already has a buffered write for id "{id}"'
                )
            self._validate_write_precondition(operation, id)

    def _validate_write_precondition(self, operation: str, id: str) -> None:
        if operation == "add" and id not in self._known_absent:
            raise InvalidArgumentError(
                f'transactional add for id "{id}" requires a prior read '
                "proving the id is absent"
            )
        if operation == "update" and id not in self._known_present:
            raise InvalidArgumentError(
                f'transactional update for id "{id}" requires a prior read '
                "proving the id is present"
            )
        if operation == "delete" and id not in self._known_present:
            raise InvalidArgumentError(
                f'transactional delete for id "{id}" requires a prior read '
                "proving the id is present"
            )


def require_conditional_http_transaction(
    transaction: object,
) -> ConditionalHttpTransaction:
    if not isinstance(transaction, ConditionalHttpTransaction):
        raise ValueError("invalid conditional transaction for HTTP client")
    return transaction

View on GitHub (pinned to aecdd12c8a)

Solutions

  1. Do an id-based get in the same transaction first and update only the ids it returned
  2. Use upsert when the record may or may not exist
  3. If the id was absent, add it after the read proves absence instead of updating

Example fix

# before
with client.transaction():
    collection.update(ids=["doc1"], metadatas=[{"v": 2}])
# after
with client.transaction():
    got = collection.get(ids=["doc1"])
    if got["ids"]:
        collection.update(ids=["doc1"], metadatas=[{"v": 2}])
    else:
        collection.add(ids=["doc1"], embeddings=[emb])
Defensive patterns

Strategy: validation

Validate before calling

def txn_update(collection, ids, **fields):
    got = collection.get(ids=ids)          # proves presence inside the txn
    present = list(got["ids"])
    if present:
        collection.update(ids=present, **fields)
    return present

Try / catch

from chromadb.errors import InvalidArgumentError

try:
    collection.update(ids=ids, metadatas=metas)
except InvalidArgumentError as e:
    if "requires a prior read" in str(e):
        got = collection.get(ids=ids)
        collection.update(ids=list(got["ids"]), metadatas=metas)
    else:
        raise

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16). Data as JSON: /api/errors/a826d8214fabec28. Report an issue: GitHub.