{"record":{"id":"55077455f9d897bb","repo":"benbjohnson/litestream","slug":"acquire-read-lock-w","errorCode":null,"errorMessage":"acquire read lock: %w","messagePattern":"acquire read lock: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"db.go","lineNumber":1098,"sourceCode":"\t\treturn fmt.Errorf(\"enable wal failed, mode=%q\", mode)\n\t}\n\n\t// Create a table to force writes to the WAL when empty.\n\t// There should only ever be one row with id=1.\n\tif _, err := db.db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS _litestream_seq (id INTEGER PRIMARY KEY, seq INTEGER);`); err != nil {\n\t\treturn fmt.Errorf(\"create _litestream_seq table: %w\", err)\n\t}\n\n\t// Create a lock table to force write locks during sync.\n\t// The sync write transaction always rolls back so no data should be in this table.\n\tif _, err := db.db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS _litestream_lock (id INTEGER);`); err != nil {\n\t\treturn fmt.Errorf(\"create _litestream_lock table: %w\", err)\n\t}\n\n\t// Start a long-running read transaction to prevent other transactions\n\t// from checkpointing.\n\tif err := db.acquireReadLock(ctx); err != nil {\n\t\treturn fmt.Errorf(\"acquire read lock: %w\", err)\n\t}\n\n\t// Read page size.\n\tif err := db.db.QueryRowContext(ctx, `PRAGMA page_size;`).Scan(&db.pageSize); err != nil {\n\t\treturn fmt.Errorf(\"read page size: %w\", err)\n\t} else if db.pageSize <= 0 {\n\t\treturn fmt.Errorf(\"invalid db page size: %d\", db.pageSize)\n\t}\n\n\t// Ensure meta directory structure exists.\n\tif err := internal.MkdirAll(db.metaPath, db.dirInfo); err != nil {\n\t\treturn err\n\t}\n\n\t// Ensure WAL has at least one frame in it.\n\tif err := db.ensureWALExists(ctx); err != nil {\n\t\treturn fmt.Errorf(\"ensure wal exists: %w\", err)\n\t}","sourceCodeStart":1080,"sourceCodeEnd":1116,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/db.go#L1080-L1116","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","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."],"exampleFix":"// before\nctx, cancel := context.WithTimeout(ctx, 50*time.Millisecond)\ndb.Open(ctx) // init cancelled -> 'acquire read lock: context deadline exceeded'\n// after\ncancel() // release; call Open with a long-lived context\ndb.Open(context.Background())","handlingStrategy":"retry","validationCode":"// ensure WAL mode and no long-running checkpoint/write jobs at startup\nsqlite3 app.db 'PRAGMA journal_mode;'  # expect: wal","typeGuard":null,"tryCatchPattern":"async function openWithRetry(db, ctx, attempts = 5) {\n  for (let i = 0; i < attempts; i++) {\n    try { return await db.Open(ctx); }\n    catch (err) {\n      if (!String(err.message).includes('acquire read lock') || i === attempts - 1) throw err;\n      await sleep(500 * 2 ** i);\n    }\n  }\n}","preventionTips":["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."],"tags":["sqlite","wal","locking","init"],"backgroundTag":"database-query-failed","analyzedSha":"4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3","analyzedAt":"2026-09-06T18:29:25.564Z","contentChangedAt":"2026-09-06T18:29:25.564Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}