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