benbjohnson/litestream · error

create _litestream_seq table

Error message

create _litestream_seq table: %w

What it means

Litestream wraps a failure to create the internal `_litestream_seq` bookkeeping table during DB initialization (db.init). This table forces a write into the WAL when the database is empty so that replication has a starting point. The error wraps the underlying SQLite driver error, so the real cause (disk full, permissions, corruption, connection failure) is in the wrapped message.

Solutions

  1. Check file system permissions and free disk space on the database volume; ensure the file is writable by the Litestream user.
  2. Ensure no other process holds the SQLite write lock (stop other writers or other litestream instances) and consider a busy_timeout.
  3. Verify the file is a valid SQLite database with `sqlite3 path 'PRAGMA integrity_check;'`.
  4. Re-open with a writable connection string/driver; if using litestream-yumrik/CGO-less driver, ensure the DSN does not set mode=ro.

Example fix

// before
litestream replicate -config /etc/litestream.yml   # db on read-only NFS mount -> fails
// after
mount -o rw ... /var/lib/app   # or chown litestream:litestream /var/lib/app/app.db
Defensive patterns

Strategy: try-catch

Validate before calling

// before opening with litestream
const os = require('os'); const fs = require('fs');
const st = fs.statSync(dbPath);
if (!fs.accessSync(dbPath, fs.constants.W_OK)) throw new Error('db not writable: ' + dbPath);
if (st.size > 0 && fs.readFileSync(dbPath).slice(0,15).toString() !== 'SQLite format 3') throw new Error('not a sqlite db');

Type guard

function isWritableSqliteFile(p) {
  try { const fd = fs.openSync(p, 'r+'); const buf = Buffer.alloc(16); fs.readSync(fd, buf, 0, 16, 0); fs.closeSync(fd); return buf.toString('latin1').startsWith('SQLite format 3'); }
  catch { return false; }
}

Try / catch

try {
  await db.Open(ctx);
} catch (err) {
  if (String(err.message).includes('create _litestream_seq table')) {
    // inspect err.cause: EACCES/ENOSPC/SQLITE_BUSY
    logger.error('litestream init failed on seq table', { cause: err.cause });
    // check disk space & permissions, then retry with backoff
  }
}

Prevention

When it happens

Trigger: Calling litestream replicate / Open on a database whose SQLite connection cannot execute the `CREATE TABLE IF NOT EXISTS _litestream_seq` statement: read-only database file or directory, disk full, corrupted schema, `database is locked` from another process holding a write lock, or a non-SQLite/invalid file at the configured path.

Common situations: Pointing Litestream at a database on a read-only mount or with wrong file permissions; another Litestream instance or heavy writer holding the write lock (SQLITE_BUSY); running out of disk space on the data volume; the path is not a valid SQLite database (e.g. empty or encrypted file).

Related errors


AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06). Data as JSON: /api/errors/fac9c444f5c2f9e6. Report an issue: GitHub.

Appendix: source

Thrown at db.go:1086

			db.db.Close()
			db.f.Close()
			db.db, db.f = nil, nil
		}
	}()

	// 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 {

View on GitHub (pinned to 4ed7a308f6)