pgref.dev/sqlite/errors/SQLITE_BUSY
SQLITE_BUSYERRORTier 2 — Caution✅ HIGH confidence

database is locked

Category: Locking & ConcurrencyVersions: All SQLite versions

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
# 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
sqlite3.OperationalError: database is locked

Fix 1: Set a busy timeout

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

fix
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.

fix
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

⚙️ This error reference was generated with AI assistance and reviewed for accuracy. Examples are provided to illustrate common scenarios and may not cover every case. Always test fixes in a development environment before applying to production. Spotted an error? Suggest a correction →