Mechanism◆Article
Table.delete_where() never wraps its DELETE in a transaction, so it fails to commit and leaves the database connection permanently poisoned, causing silent data loss on subsequent operations.
Claude Fable's pre-release review found that Table.delete_where() executes its DELETE without an atomic() wrapper, leaving the connection stuck 'in_transaction', which silently rolls back that delete plus any later writes—even to unrelated tables—when the connection closes. ✦ AI generated
Claude Fable · Simon Willison's Weblog · 2026-07-05 · original ↗
Elicited by
“Final review before shipping a stable 4.0 release - very important to spot any last minute things that would be a breaking change if we fix them later”
Table.delete_where() (sqlite_utils/db.py:2948) runs its DELETE via a bare self.db.execute() with no atomic() wrapper — compare Table.delete() at db.py:2944, which wraps correctly. The connection is left in_transaction=True, so every subsequent atomic() call takes the savepoint branch (db.py:430-440) and never commits either.
Read full article ↗excerpt · fair-use quotation
- ·delete_where() never wraps DELETE in atomic()
- ·Connection left permanently in_transaction=True
- ·Delete and later writes silently roll back on close
- ·Found in Claude Fable's pre-release review
- ·delete_where() (db.py:2948) uses bare self.db.execute()
- ·delete() (db.py:2944) correctly wraps with atomic()
- ·Bare execute() leaves connection stuck mid-transaction
- ·Later atomic() calls hit savepoint branch (db.py:430-440)
- ·Those calls never commit either
- ·Even unrelated table writes vanish silently
Around this claim