chroma-core/chroma · error · ValueError

txn.commit() cannot be called inside run()

Error message

txn.commit() cannot be called inside run()

What it means

Chroma's ConditionalCollectionTransaction.run(callback) owns the commit lifecycle: while the callback executes it sets an internal flag (_commit_blocked_by_run, chromadb/api/models/ConditionalCollectionTransaction.py:59) and after the callback returns it commits itself, retrying with a fresh transaction on ConditionalWriteConflictError/StaleReadError/BackoffError. txn.commit() raises this ValueError when invoked inside that callback, because a manual commit would double-commit buffered writes and defeat run()'s retry semantics.

Source

Thrown at chromadb/api/models/ConditionalCollectionTransaction.py:274

        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")

        self._run_transaction_operation(
            lambda: 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,
            )
        )

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

View on GitHub (pinned to aecdd12c8a)

Solutions

  1. Delete the txn.commit() call inside the run() callback - run() commits automatically once the callback returns successfully
  2. If you need explicit commit control, do not use run(): call txn.add/upsert/update/delete directly and then txn.commit() yourself
  3. Move conditional commit logic into the callback (return early or raise to abort), letting run() decide when to commit

Example fix

# before
txn.run(lambda t: (t.add(ids=['a'], embeddings=[[0.1]]), t.commit()))

# after - run() commits automatically
txn.run(lambda t: t.add(ids=['a'], embeddings=[[0.1]]))
Defensive patterns

Strategy: try-catch

Try / catch

try:
    txn.run(callback)
except ValueError as e:
    if 'cannot be called inside run()' in str(e):
        # callback invoked txn.commit() itself; remove that call
        raise RuntimeError('callback must not call txn.commit(); run() commits automatically')
    raise

Prevention

When it happens

Trigger: Calling t.commit() on the transaction handle passed into the run() callback, e.g. txn.run(lambda t: (t.add(ids=['a'], embeddings=[[0.1]]), t.commit())) or a named callback whose last line is t.commit(). commit() is only legal on a transaction used manually (get/add/upsert/update/delete then commit()), never on one inside an active run() block.

Common situations: Porting manual-transaction code (begin -> ops -> commit) into run() and leaving the old commit() in place; trying to commit conditionally mid-callback; following older examples that predate run()'s auto-commit; wrapping run() in a helper that also commits.

Related errors


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