Environment
- Python: 3.14.3
- SQLite: (system default)
- code-review-graph: (installed version)
- OS: Linux
Steps to reproduce
- Have a working tree where
git diff --name-only HEAD~1 returns a mix of deleted files and modified/added files (e.g. a branch with file deletions plus code changes).
- Run
code-review-graph update.
Error
sqlite3.OperationalError: cannot start a transaction within a transaction
Full traceback:
File "code_review_graph/tools/build.py", line 311, in build_or_update_graph
result = incremental_update(root, store, base=base)
File "code_review_graph/incremental.py", line 515, in incremental_update
store.store_file_nodes_edges(str(repo_root / rel_path), nodes, edges, fhash)
File "code_review_graph/graph.py", line 239, in store_file_nodes_edges
self._conn.execute("BEGIN IMMEDIATE")
sqlite3.OperationalError: cannot start a transaction within a transaction
Root cause
GraphStore.__init__ opens the connection with the default isolation_level="". Python's sqlite3 module automatically issues an implicit BEGIN DEFERRED before the first DML statement in that mode.
In incremental_update (and full_build), when deleted files are detected (line 471 / line 356), store.remove_file_data() issues DELETE statements that silently open an implicit DEFERRED transaction. This transaction is never committed before store_file_nodes_edges is called for the next file, which then tries BEGIN IMMEDIATE — SQLite rejects nesting an explicit IMMEDIATE inside an already-open DEFERRED transaction.
Note: the same issue exists in full_build (lines 355–356 call remove_file_data on stale files before the parse loop).
Suggested fix
In graph.py, store_file_nodes_edges, flush any pending implicit transaction before issuing BEGIN IMMEDIATE:
def store_file_nodes_edges(self, file_path, nodes, edges, fhash=""):
"""Atomically replace all data for a file."""
# Flush any implicit transaction started by a prior DML call (e.g.
# remove_file_data DELETE statements). Python's sqlite3 auto-begins a
# DEFERRED transaction on the first DML; BEGIN IMMEDIATE inside an active
# transaction raises OperationalError on Python 3.12+.
if self._conn.in_transaction:
self._conn.commit()
self._conn.execute("BEGIN IMMEDIATE")
...
Connection.in_transaction is available since Python 3.2.
Environment
Steps to reproduce
git diff --name-only HEAD~1returns a mix of deleted files and modified/added files (e.g. a branch with file deletions plus code changes).code-review-graph update.Error
Full traceback:
Root cause
GraphStore.__init__opens the connection with the defaultisolation_level="". Python'ssqlite3module automatically issues an implicitBEGIN DEFERREDbefore the first DML statement in that mode.In
incremental_update(andfull_build), when deleted files are detected (line 471 / line 356),store.remove_file_data()issuesDELETEstatements that silently open an implicitDEFERREDtransaction. This transaction is never committed beforestore_file_nodes_edgesis called for the next file, which then triesBEGIN IMMEDIATE— SQLite rejects nesting an explicitIMMEDIATEinside an already-openDEFERREDtransaction.Note: the same issue exists in
full_build(lines 355–356 callremove_file_dataon stale files before the parse loop).Suggested fix
In
graph.py,store_file_nodes_edges, flush any pending implicit transaction before issuingBEGIN IMMEDIATE:Connection.in_transactionis available since Python 3.2.