benbjohnson/litestream · error
create _litestream_lock table: %w
Error message
create _litestream_lock table: %w
What it means
Litestream failed to create the `_litestream_lock` table during DB initialization. This table is used to force write locks during sync; sync transactions always roll back so no data remains in it. As with the seq-table error, the underlying driver error is wrapped and carries the real cause.
Source
Thrown at db.go:1092
// Enable WAL and ensure it is set. New mode should be returned on success:
// https://www.sqlite.org/pragma.html#pragma_journal_mode
var mode string
if err := db.db.QueryRowContext(ctx, `PRAGMA journal_mode = wal;`).Scan(&mode); err != nil {
return err
} else if mode != "wal" {
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 errView on GitHub (pinned to 4ed7a308f6)
Solutions
- Ensure only one Litestream process monitors each database path.
- Check/raise busy_timeout and retry while the conflicting writer finishes.
- Verify disk space and write permissions on the database directory.
- Run `PRAGMA integrity_check` to rule out corruption; restore from backup if corrupted.
Example fix
// before # two litestream daemons started (systemd + manual) -> 'database is locked' // after systemctl stop litestream; pkill -f 'litestream replicate'; systemctl start litestream
Defensive patterns
Strategy: try-catch
Validate before calling
// ensure single writer: check for competing litestream processes and file writability fs.accessSync(dbPath, fs.constants.W_OK);
Type guard
function canAcquireWrite(path) { try { return fs.accessSync(path, fs.constants.W_OK) === undefined; } catch { return false; } } Try / catch
try {
await db.Open(ctx);
} catch (err) {
if (String(err.message).includes('create _litestream_lock table')) {
if (String(err.cause).includes('locked')) { await sleep(backoff); retry(); }
else throw err;
}
} Prevention
- Use process supervision (systemd) with a singleton so only one litestream runs.
- Keep application write transactions short to avoid prolonged SQLITE_BUSY.
- Set a generous busy_timeout on the SQLite connection.
- Watch for disk-full alerts.
When it happens
Trigger: The `CREATE TABLE IF NOT EXISTS _litestream_lock` ExecContext fails during db.init — same class of causes as the seq table: read-only file, SQLITE_BUSY from a concurrent writer, disk full, corrupted database, or the previous seq-table creation succeeded but state changed mid-init.
Common situations: Concurrent litestream processes on the same database racing through init; database locked by a long-running application write transaction; disk quota exceeded right after seq table creation; migrating from an older Litestream version with stale state.
Related errors
- create _litestream_seq table: %w
- acquire read lock: %w
- open database: %w
- open database: %w
- set journal mode: %w
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/71279031b4faa487.
Report an issue: GitHub.