SQLite database is locked, explained and fixed

database is locked is the message for SQLite's SQLITE_BUSY result: an operation needed access the database's locking state does not currently allow. SQLite is a single-file, embedded database that permits many readers but exactly one writer at a time, coordinated by locks on the file. A writer that finds another write transaction in progress (or a reader that finds the file mid-commit in the default journal mode) is refused, immediately unless a busy timeout is set. The error therefore always means concurrency is happening, or a transaction somewhere was opened and never finished.

Find the holder

  1. Another process is the first suspect: a second instance of the application, a database browser tool left open with an uncommitted transaction, a backup or sync client copying the file mid-write. Closing the forgotten tool is a real and frequent resolution.
  2. Your own application is the second: a connection that began a transaction and has not committed or rolled back (an exception path that skips cleanup is the classic), while another connection in the same process tries to write. Connection-per-thread designs hit this internally without any outside help.

The fixes, in order of reach

  1. Set a busy timeout everywhere: PRAGMA busy_timeout = 5000; (or the driver's equivalent setting) makes SQLite wait and retry for that many milliseconds instead of failing instantly. Most real contention is milliseconds long; this one setting removes most occurrences and is the correct first move.
  2. Enable WAL mode for concurrent applications: PRAGMA journal_mode = WAL; lets readers and the single writer coexist far more gracefully than the default rollback journal, which blocks readers during writes. WAL is the standard configuration for multi-process and multi-threaded use of one file.
  3. Keep write transactions short and always closed: begin, write, commit promptly; never hold a transaction open across user think-time, network calls, or long computations; ensure error paths roll back (framework transaction wrappers and using-blocks exist for this).
  4. Serialize writes by design in heavy cases: route writes through one connection or a queue in the application when the workload is write-intensive, matching the application's shape to the database's single-writer nature.
  5. Keep the file on a local filesystem: SQLite's locking depends on filesystem semantics that network shares (SMB, NFS) implement unreliably; a database on a network drive manufactures lock errors. The documentation is explicit that this configuration is unsafe.

Common questions

Why does it happen in tests that run in parallel? Each test process opens the same file and their write transactions collide, with no busy timeout configured the first collision fails a test. Per-test database files, WAL plus a timeout, or serialized writes are the standard remedies.

Is my database corrupted when I see this? No. Locking refusals are the mechanism working, not damage. Corruption has different symptoms and messages; a locked database becomes fully available the moment the holding transaction ends.

Does WAL remove the single-writer limit? No. WAL improves reader-writer coexistence and reduces lock friction, but there is still exactly one writer at a time. busy_timeout remains necessary so writers queue briefly instead of failing.

Related errors

SQL Server error 1205 (deadlock victim) · MySQL error 1062 (Duplicate entry) · MongoDB: Server selection timed out