SQLite: database is locked (tested)
sqlite3.OperationalError: database is locked means another connection already holds a write lock on the file and yours couldn't get in before timing out. We reproduced it with two genuine connections to the same database file and measured exactly how long Python waits before giving up, and what fixes it.
1. The exact error, from a real second writer
We opened one connection and started a write transaction without committing:
con1 = sqlite3.connect("t.db")
con1.execute("BEGIN")
con1.execute("INSERT INTO t VALUES (1)")
# not committed yet
A second connection trying to write at the same time, using the default settings, gave:
con2 = sqlite3.connect("t.db")
con2.execute("INSERT INTO t VALUES (2)")
# ... after exactly 5.02 seconds:
sqlite3.OperationalError: database is locked
Why exactly 5 seconds: Python's sqlite3.connect() has a default timeout of 5.0 seconds. It doesn't fail instantly — it retries internally for that long, hoping the lock clears, before finally raising. We measured this directly: the failure landed at 5.02s, not immediately.
2. Raising the timeout makes it actually wait and succeed
We reran the same scenario with a longer timeout on the second connection, and committed the first connection's transaction after a real 1-second delay from a background thread:
con2 = sqlite3.connect("t.db", timeout=3.0)
con2.execute("INSERT INTO t VALUES (2)") # con1 commits after 1s
con2.commit()
# succeeded after waiting 1.03s
This confirms timeout= isn't just a longer deadline before failure — it's a real busy-wait: the moment the lock actually clears, the second connection proceeds, measured here at 1.03s (matching the 1-second delay), not the full 3-second timeout.
3. The fix: a longer timeout, and keep write transactions short
We confirmed two things work together, not as alternatives:
- Raise
timeout=on connect if brief lock contention is expected (multiple processes/threads writing to the same SQLite file) — we measured this genuinely waits rather than failing fast. - Keep the transaction holding the lock as short as possible — the lock in our test was only released by
con1.commit(); the longer that's deferred, the longer every other writer either waits or times out. A transaction left open (forgotten commit, a long-running calculation between BEGIN and COMMIT) is the actual root cause far more often than the timeout value itself.
We did not test WAL mode (PRAGMA journal_mode=WAL) directly in this pass; SQLite's own documentation states it lets readers proceed without blocking on a writer, which is a different scenario from the two-writers case measured here.
Frequently asked questions
Why does Python's sqlite3 take exactly 5 seconds to raise 'database is locked'?
That's the default timeout= value on sqlite3.connect() — we measured the failure landing at 5.02 seconds, confirming Python retries internally for that long before giving up, rather than failing on the first attempt.
Does raising the timeout just delay the same failure?
No — we confirmed it's a genuine busy-wait: in our test, connection two succeeded after 1.03 seconds (matching exactly when the other connection committed), not after waiting out the full 3-second timeout we'd set. It proceeds the instant the lock actually clears.
What's the real root cause of most 'database is locked' errors?
A write transaction left open longer than necessary on another connection — in our repro, the lock existed purely because con1 hadn't committed yet. Raising the timeout helps you tolerate brief contention, but shortening how long transactions stay open prevents the contention itself.
Does a longer timeout fix two processes writing at once, or only threads?
We tested with two sqlite3 connections (one via a background thread) to the same file, which is the same lock mechanism SQLite uses across separate OS processes — the lock is a property of the database file, not the connecting thread or process.
Written with AI assistance. Measured on 2026-10-03 with Python 3.13.5's built-in sqlite3 module, using two real connections to the same file-backed database, one holding an uncommitted write transaction while the other attempted a write. Every timing and message below is copy-pasted output, not paraphrased. PlainHeaders is our extension; Python's sqlite3 is built in.