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
- Delete the txn.commit() call inside the run() callback - run() commits automatically once the callback returns successfully
- If you need explicit commit control, do not use run(): call txn.add/upsert/update/delete directly and then txn.commit() yourself
- 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
- Never call txn.commit() inside a run() callback - run() owns the commit and retries with a fresh transaction on conflict
- Use run() for auto-retrying optimistic transactions; use manual ops + commit() only when you skip run() entirely
- Keep callbacks side-effect-light: buffer reads/writes, return data, and let run() commit
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
- conditional transaction is closed
- conditional transaction cannot span collections
- Conditional transactions are only supported when connecting
- Expected collection name that (1) contains 3-63 characters,
- Database name must be at least 3 characters long
AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16).
Data as JSON: /api/errors/53425dae409a8982.
Report an issue: GitHub.