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
- Delete the `commit()` call from inside the callback; `run()` commits automatically when the callback returns
- Return a value from the callback to signal outcomes; raise to abort without committing
- 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
- Treat run() callbacks as commit-free: the library commits after the callback returns
- Use explicit begin/commit transactions only outside run()
- Signal early exit by raising or returning from the callback, never by committing
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
- ids must be provided for transactional delete
- Conditional transactions are only supported when connecting
- Could not connect to a Chroma server. Are you sure it is run
- Could not connect to tenant {tenant}. Are you sure it exists
- conditional transaction has no collection scope
AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16).
Data as JSON: /api/errors/c232621bab3060e5.
Report an issue: GitHub.