chroma-core/chroma · error · ValueError

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

Error message

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

What it means

`run(callback)` sets an internal `_commit_blocked_by_run` flag and commits automatically after the callback returns successfully, retrying the whole callback on write conflicts. Calling `txn.commit()` from inside the callback would double-commit and break the retry protocol, so it is rejected with ValueError.

Source

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

        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. Delete the `commit()` call from inside the callback; `run()` commits automatically when the callback returns
  2. Return a value from the callback to signal outcomes; raise to abort without committing
  3. If you truly need manual commit control, skip `run()` and use the explicit transaction flow (begin, operate, `commit()` outside any callback)

Example fix

# before
async def work(txn):
    await txn.upsert(...)
    await txn.commit()  # ValueError: inside run()
await collection.transaction.run(work)

# after
async def work(txn):
    await txn.upsert(...)
    # run() commits automatically on return
await collection.transaction.run(work)
Defensive patterns

Strategy: try-catch

Try / catch

try:
    await txn.commit()
except ValueError as e:
    if "cannot be called inside run()" in str(e):
        # structural bug: remove the commit; run() commits on callback return
        raise

Prevention

When it happens

Trigger: Invoking `await txn.commit()` inside the callback passed to `collection.transaction.run(lambda txn: ...)` on an AsyncCollection, e.g. to make an early exit or partial save.

Common situations: Copy-pasting manual-transaction code (begin / do work / commit) into a `run()` block, or trying to conditionally commit halfway through a callback.

Related errors


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