SQLITE_BUSYERRORTier 2 — Caution✅ HIGH confidencedatabase is locked
What this means
SQLITE_BUSY (result code 5) is returned when a write operation cannot acquire the necessary database-level lock because another connection holds a conflicting lock. Unlike SQLITE_LOCKED which is object-level, SQLITE_BUSY is a database-file-level conflict. It is common in multi-process or multi-threaded environments where each process opens its own connection to the same database file.
Why it happens
- 1Another process has the database open with a write transaction in progress
- 2A long-running reader in WAL mode has created a snapshot that prevents a checkpoint
- 3The application opened a BEGIN IMMEDIATE or BEGIN EXCLUSIVE transaction on one connection while another connection holds a read transaction
- 4The busy timeout is set to 0 (default) and no retry logic is implemented
How to reproduce
Two Python processes both open the same SQLite file and attempt concurrent writes without a busy timeout.
# Process 1
import sqlite3
conn1 = sqlite3.connect('shared.db')
conn1.execute('BEGIN EXCLUSIVE')
# Process 2 (simultaneously, in another terminal)
import sqlite3
conn2 = sqlite3.connect('shared.db')
conn2.execute('BEGIN EXCLUSIVE') # triggers SQLITE_BUSYFix 1: Set a busy timeout
When transient lock contention is expected and automatic retry is acceptable.
import sqlite3
conn = sqlite3.connect('shared.db')
conn.execute('PRAGMA busy_timeout = 5000') # wait up to 5 seconds
# Or use the isolation_level parameter:
conn = sqlite3.connect('shared.db', timeout=5.0)Why this works
busy_timeout instructs the SQLite library to sleep and retry the lock acquisition for up to the specified number of milliseconds before returning SQLITE_BUSY. The Python sqlite3 module exposes this as the timeout parameter on connect().
Fix 2: Enable WAL mode for better read/write concurrency
When multiple readers and one writer need to operate simultaneously.
import sqlite3
conn = sqlite3.connect('shared.db')
conn.execute('PRAGMA journal_mode=WAL')
conn.commit()Why this works
WAL (Write-Ahead Logging) mode separates readers from writers: readers read the last committed snapshot in the WAL file while a writer appends to the WAL. This eliminates most SQLITE_BUSY errors caused by reader-writer conflicts because readers never block writers and writers never block readers.
What not to do
Retry in a tight loop without sleep
Why it's wrong: Hammering lock acquisition wastes CPU and increases contention. Use busy_timeout to let SQLite handle retries with exponential back-off internally.
Version notes
SQLite 3.7.0+WAL mode introduced. Strongly recommended for any multi-connection workload.Dangerous variant
⚠️ Warning
SQLITE_BUSY_SNAPSHOT (517) — returned in WAL mode when a write transaction cannot be upgraded because another connection still holds a read snapshot from before the pending write. Requires the reader connection to be closed or re-opened.
Sources
📚 Official docs: https://www.sqlite.org/rescode.html#busy
🔧 Source ref: sqlite3.h — SQLITE_BUSY = 5
📖 Further reading: SQLite WAL mode documentation
📖 Further reading: SQLite locking and concurrency
Confidence assessment
✅ HIGH confidence
Well-documented and stable. WAL mode behaviour is covered extensively in the official docs. busy_timeout semantics are consistent across all platforms.
See also
🔗 Related errors
📄 Reference pages