SQLite: cannot start a transaction within a transaction (tested)

sqlite3.OperationalError: cannot start a transaction within a transaction means a connection already has an open transaction when something calls BEGIN again. We reproduced the obvious cause and a sneaky one Python creates for you without asking.

1. The obvious cause: BEGIN called twice

con.execute("BEGIN")
con.execute("BEGIN")
sqlite3.OperationalError: cannot start a transaction within a transaction

We confirmed this happens identically in autocommit mode (isolation_level=None) too — it's not specific to Python's default transaction handling.

2. The sneaky cause: Python already opened one for you

With default settings (no isolation_level=None), we ran a plain INSERT with no explicit BEGIN at all:

con.execute("INSERT INTO t VALUES (1)")
print(con.in_transaction)   # True — a transaction is already open

Measured: Python's sqlite3 silently opened a transaction the moment the INSERT ran, with no BEGIN in the code at all. A later explicit BEGIN, written by someone who assumed none was open yet, then fails with the identical error:

con.execute("BEGIN")
sqlite3.OperationalError: cannot start a transaction within a transaction

This is the version that actually bites people: code that mixes "let Python manage transactions implicitly" with "manage transactions explicitly" in the same connection, often because a library call did the first INSERT before your own explicit BEGIN got a chance to run.

3. The fix: check before you BEGIN, or pick one style consistently

Check whether a transaction is already open before starting one explicitly:

if not con.in_transaction:
    con.execute("BEGIN")

We confirmed con.in_transaction correctly reports True right after the implicit open, with no explicit BEGIN needed to set it. The more durable fix is picking one transaction style per connection and sticking to it: either let Python's default implicit handling manage commits/rollbacks, or set isolation_level=None and issue every BEGIN/COMMIT yourself — not both.

Frequently asked questions

Does this error only happen if my code calls BEGIN twice?

No — we measured a sneakier case: Python's sqlite3 opens a transaction implicitly on the first write statement even with no BEGIN in your code. A later explicit BEGIN, written assuming none was open, fails identically.

How do I check if a transaction is already open before starting one?

con.in_transaction — we confirmed this reports True immediately after Python's implicit transaction open, with no explicit BEGIN required to set it, so checking it before calling BEGIN avoids the error.

Does isolation_level=None (autocommit mode) avoid this error?

No — we tested it specifically: a double BEGIN in autocommit mode fails with the identical error. Autocommit mode changes when Python opens transactions implicitly, not whether nesting BEGIN calls is allowed.

What's the real-world cause of this, not just a deliberate double BEGIN?

Mixing transaction styles on one connection — code that does a plain write (which opens a transaction implicitly under default settings) and later calls BEGIN explicitly, assuming no transaction was open yet. We reproduced this exact sequence.

What's the safest fix long-term?

Pick one transaction-management style per connection and don't mix them — either rely on Python's default implicit commit/rollback entirely, or set isolation_level=None and issue every BEGIN/COMMIT yourself. We confirmed the error only arises from mixing the two.

Written with AI assistance. Measured on 2026-10-03 with Python 3.13.5's built-in sqlite3 module, triggering the error from an explicit double BEGIN, an autocommit-mode (isolation_level=None) double BEGIN, and the sneaky case: an explicit BEGIN issued after a transaction Python already opened implicitly. Every error string and con.in_transaction value below is copy-pasted output, not paraphrased. PlainHeaders is our extension; Python's sqlite3 is built in.