chroma-core/chroma · error · ValueError

ids must be provided for transactional delete

Error message

ids must be provided for transactional delete

What it means

Raised by AsyncConditionalCollectionTransaction.delete(). Conditional (optimistic) transactions only support delete-by-ID: the wrapper builds the delete request with where/where_document forced to None, then requires the validated 'ids' field to be non-None. Predicate deletes are an explicit limitation of the transaction API, so any ids value that unpacks to None is rejected before the server is called.

Source

Thrown at chromadb/api/models/AsyncConditionalCollectionTransaction.py:258

            self._collection._client._conditional_upsert(
                transaction=self._transaction,
                collection_id=self._collection.id,
                ids=upsert_request["ids"],
                embeddings=upsert_request["embeddings"],
                metadatas=upsert_request["metadatas"],
                documents=upsert_request["documents"],
                uris=upsert_request["uris"],
                tenant=self._collection.tenant,
                database=self._collection.database,
            )
        )

    async def delete(self, ids: OneOrMany[ID]) -> None:
        delete_request = self._collection._validate_and_prepare_delete_request(
            ids, None, None
        )
        if delete_request["ids"] is None:
            raise ValueError("ids must be provided for transactional delete")

        await self._run_transaction_operation(
            self._collection._client._conditional_delete(
                transaction=self._transaction,
                collection_id=self._collection.id,
                ids=delete_request["ids"],
                tenant=self._collection.tenant,
                database=self._collection.database,
            )
        )

    async def commit(self) -> ConditionalCommitResult:
        if self._commit_blocked_by_run:
            raise ValueError("txn.commit() cannot be called inside run()")
        return await self._commit_after_run()

View on GitHub (pinned to aecdd12c8a)

Solutions

  1. Pass an explicit list of concrete string IDs: `await txn.delete(["id-1", "id-2"])`
  2. If you need delete-by-filter, first `txn.get(where=..., include=[])` inside the transaction to collect IDs, then delete those IDs in the same transaction
  3. If predicate semantics are required and transactional guarantees are not, use the non-transactional `collection.delete(where=..., where_document=...)`
  4. Check the ids variable is a non-empty list before entering `run()`

Example fix

// before
await txn.delete(ids=None)  # or a variable that is None at runtime

// after
if not ids:
    raise ValueError("cannot delete without ids inside a transaction")
await txn.delete(ids=ids)
Defensive patterns

Strategy: validation

Validate before calling

ids = ["a", "b"]
if ids is None or len(list(ids)) == 0:
    raise ValueError("transactional delete requires explicit non-empty ids")
await txn.delete(ids=list(ids))

Type guard

from typing import Any, List, TypeGuard

def is_non_empty_id_list(v: Any) -> TypeGuard[List[str]]:
    return isinstance(v, (list, tuple)) and len(v) > 0 and all(isinstance(i, str) for i in v)

Prevention

When it happens

Trigger: Calling `await txn.delete(ids)` on a transaction obtained from an AsyncCollection where `ids` survives the 'at least one parameter' presence check but validates/unpacks to a None ids list (e.g. a None-equivalent or empty/malformed value passed through maybe_cast_one_to_many).

Common situations: Porting `collection.delete(where=...)` / `delete(where_document=...)` calls into `collection.transaction.run(...)`, or passing a variable that is None at runtime inside the run callback.

Related errors


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