SQLITE_BUSY
SQLiteERRORNotableLocking & ConcurrencyHIGH confidence

database 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
  1. 1Another process has the database open with a write transaction in progress
  2. 2A long-running reader in WAL mode has created a snapshot that prevents a checkpoint
  3. 3The application opened a BEGIN IMMEDIATE or BEGIN EXCLUSIVE transaction on one connection while another connection holds a read transaction
  4. 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.

trigger — this will error
trigger — this will error
# 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_BUSY

expected output

sqlite3.OperationalError: database is locked

Fix 1

Set a busy timeout

WHEN When transient lock contention is expected and automatic retry is acceptable.

Set a busy timeout
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 When multiple readers and one writer need to operate simultaneously.

Enable WAL mode for better read/write concurrency
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

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.

Content generated with AI assistance and reviewed for accuracy. Found an error? hello@errcodes.dev

← All SQLite errors