benbjohnson/litestream · error
acquire read lock: %w
Error message
acquire read lock: %w
What it means
Litestream could not start its long-running read transaction (db.acquireReadLock) after creating internal tables. This read transaction prevents other connections from checkpointing the WAL while Litestream observes it. Failure here is a wrapped driver error, most commonly `database is locked`/SQLITE_BUSY or a context cancellation.
Source
Thrown at db.go:1098
return fmt.Errorf("enable wal failed, mode=%q", mode)
}
// Create a table to force writes to the WAL when empty.
// There should only ever be one row with id=1.
if _, err := db.db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS _litestream_seq (id INTEGER PRIMARY KEY, seq INTEGER);`); err != nil {
return fmt.Errorf("create _litestream_seq table: %w", err)
}
// Create a lock table to force write locks during sync.
// The sync write transaction always rolls back so no data should be in this table.
if _, err := db.db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS _litestream_lock (id INTEGER);`); err != nil {
return fmt.Errorf("create _litestream_lock table: %w", err)
}
// Start a long-running read transaction to prevent other transactions
// from checkpointing.
if err := db.acquireReadLock(ctx); err != nil {
return fmt.Errorf("acquire read lock: %w", err)
}
// Read page size.
if err := db.db.QueryRowContext(ctx, `PRAGMA page_size;`).Scan(&db.pageSize); err != nil {
return fmt.Errorf("read page size: %w", err)
} else if db.pageSize <= 0 {
return fmt.Errorf("invalid db page size: %d", db.pageSize)
}
// Ensure meta directory structure exists.
if err := internal.MkdirAll(db.metaPath, db.dirInfo); err != nil {
return err
}
// Ensure WAL has at least one frame in it.
if err := db.ensureWALExists(ctx); err != nil {
return fmt.Errorf("ensure wal exists: %w", err)
}View on GitHub (pinned to 4ed7a308f6)
Solutions
- Retry opening after the conflicting write/checkpoint completes; stagger Litestream start vs application start.
- Remove timeout/short-lived contexts from the Open call so init is not cancelled mid-lock.
- Avoid WAL-checkpointing jobs (e.g. `PRAGMA wal_checkpoint(TRUNCATE)` schedulers) during Litestream startup.
- Do not place SQLite databases on NFS; use local disk.
Example fix
// before ctx, cancel := context.WithTimeout(ctx, 50*time.Millisecond) db.Open(ctx) // init cancelled -> 'acquire read lock: context deadline exceeded' // after cancel() // release; call Open with a long-lived context db.Open(context.Background())
Defensive patterns
Strategy: retry
Validate before calling
// ensure WAL mode and no long-running checkpoint/write jobs at startup sqlite3 app.db 'PRAGMA journal_mode;' # expect: wal
Try / catch
async function openWithRetry(db, ctx, attempts = 5) {
for (let i = 0; i < attempts; i++) {
try { return await db.Open(ctx); }
catch (err) {
if (!String(err.message).includes('acquire read lock') || i === attempts - 1) throw err;
await sleep(500 * 2 ** i);
}
}
} Prevention
- Pass a long-lived, non-expiring context to Open.
- Don't schedule TRUNCATE checkpoints or VACUUM at process startup.
- Start Litestream before or after heavy app write phases, not concurrently with them.
- Keep databases off NFS.
When it happens
Trigger: db.init calls acquireReadLock and the BEGIN/read-transaction cannot be established: another process holds a write lock or is checkpointing, the context passed to Open is cancelled/timed out, or the connection is in a bad state after the earlier CREATE TABLE statements.
Common situations: Starting litestream replicate while a backup job or `VACUUM`/checkpoint is running; application with many writers starving readers; Open called with an already-expired context; databases on network filesystems with flaky locking (NFS).
Understand the failure class
Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.
Related errors
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/55077455f9d897bb.
Report an issue: GitHub.