SQLite "database is locked" in CI
SQLite allows only one writer at a time. When two processes/threads try to write at once, the loser gets "database is locked" (SQLITE_BUSY). In CI this is usually parallel test workers contending for the same file.
What this error means
Tests or migrations fail intermittently with "database is locked", especially when run in parallel. The failure tracks concurrency, not database readiness.
sqlite3.OperationalError: database is locked
# or
Error: SQLITE_BUSY: database is lockedCommon causes
Concurrent writers on one file
Parallel test workers (or a writer plus a long read) contend for SQLite's single write lock, and the loser fails immediately by default.
No busy timeout configured
Without a busy timeout, SQLite returns SQLITE_BUSY instantly instead of waiting briefly for the lock to free.
Default rollback journal mode
The default journal mode serializes readers and writers more aggressively than WAL, increasing contention under parallelism.
How to fix it
Enable WAL and a busy timeout
WAL lets readers and a writer coexist; a busy timeout makes writers wait for the lock instead of failing instantly.
PRAGMA journal_mode=WAL;
PRAGMA busy_timeout=5000;Reduce write concurrency
- Give each parallel worker its own database file when tests allow.
- Or serialize write-heavy tests so they do not contend for one file.
- Keep transactions short so the write lock is held briefly.
How to prevent it
- Use WAL mode and a busy timeout for any concurrent SQLite access.
- Isolate per-worker database files in parallel test runs.
- On managed runners (Latchkey), self-healing auto-retries transient lock-contention failures, which buys time for a brief SQLITE_BUSY to clear.