SQLite "database is locked" / SQLITE_BUSY: diagnose the writer first

Scope: SQLite 3.x connection and transaction contention. SQLITE_BUSY usually means another connection or process holds a conflicting lock; SQLITE_LOCKED is a distinct same-connection or shared-cache condition. SQLite permits concurrent readers but only one writer.

Diagnose: identify long write transactions, statements or cursors that were not finalized, missing COMMIT or ROLLBACK paths, and sessions retained across requests. Make write transactions short, make connection ownership explicit, and serialize competing writers. A finite busy_timeout can absorb brief contention. Retry only idempotent work, with a limit and jitter. BEGIN IMMEDIATE moves lock acquisition to the start of a transaction; it does not remove contention. WAL can improve read/write overlap on one host, but still has one writer and requires checkpoint management.

Do not delete -wal or -shm files while the database is active, copy only the main DB file, use WAL on a network filesystem, disable journaling as a repair, or hide a persistent writer behind an unlimited timeout. Official supporting pages: https://sqlite.org/lang_transaction.html and https://sqlite.org/pragma.html#pragma_busy_timeout.