{"record":{"id":"6dca644e0b8e3c82","repo":"benbjohnson/litestream","slug":"enable-wal-failed-mode-q","errorCode":null,"errorMessage":"enable wal failed, mode=%q","messagePattern":"enable wal failed, mode=%q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"db.go","lineNumber":1080,"sourceCode":"\n\t// Ensure database is closed if init fails.\n\t// Initialization can retry on next sync.\n\tdefer func() {\n\t\tif err != nil {\n\t\t\t_ = db.releaseReadLock()\n\t\t\tdb.db.Close()\n\t\t\tdb.f.Close()\n\t\t\tdb.db, db.f = nil, nil\n\t\t}\n\t}()\n\n\t// Enable WAL and ensure it is set. New mode should be returned on success:\n\t// https://www.sqlite.org/pragma.html#pragma_journal_mode\n\tvar mode string\n\tif err := db.db.QueryRowContext(ctx, `PRAGMA journal_mode = wal;`).Scan(&mode); err != nil {\n\t\treturn err\n\t} else if mode != \"wal\" {\n\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)","sourceCodeStart":1062,"sourceCodeEnd":1098,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/db.go#L1062-L1098","documentation":"Litestream requires the database to be in WAL journal mode; during init it runs `PRAGMA journal_mode = wal;` and verifies the returned mode is exactly \"wal\". SQLite accepts the PRAGMA but may keep a different mode (e.g. \"delete\", \"truncate\", or returning the old mode when the switch fails), so litestream fails explicitly rather than replicating a non-WAL database it cannot monitor correctly.","triggerScenarios":"DB.init: QueryRowContext of `PRAGMA journal_mode = wal;` succeeds but Scan returns a mode string other than \"wal\". Classic cause: the database is opened in read-only mode or on a filesystem where WAL is unsupported (e.g. some network filesystems like older NFS), or the journal mode switch was rejected because of an active transaction on another connection.","commonSituations":"Placing the SQLite database on NFS/SMB/network mounts that do not support WAL; the file being opened read-only (read-only filesystem or lack of write permission on the directory needed to create -wal/-shm); another process holding the database in a mode/transaction that blocks the switch; memory-constrained or unusual VFS setups.","solutions":["Ensure litestream (and the db file's directory) has write access so WAL/-shm files can be created; test `sqlite3 /path/to/db 'PRAGMA journal_mode=wal;'` manually","Move the database to a local filesystem that supports WAL (ext4/xfs/apfs/ntfs) instead of NFS/SMB/network shares","Verify the journal mode is persistently WAL: `sqlite3 /path/to/db 'PRAGMA journal_mode;'` — if not, set it once with the app's connections closed","Close other connections/transactions that hold the database and restart litestream (init retries on next sync)"],"exampleFix":"-- before (on a db stuck in delete mode)\nPRAGMA journal_mode;  -- returns 'delete'\n-- after\nPRAGMA journal_mode=wal;  -- verify it returns 'wal' before starting litestream\n","handlingStrategy":"validation","validationCode":"// run before starting litestream:\n//   sqlite3 /path/to/db 'PRAGMA journal_mode;'\n// must print 'wal'. If not:\n//   sqlite3 /path/to/db 'PRAGMA journal_mode=wal;'\nmode, err := queryScalar(db, \"PRAGMA journal_mode\")\nif err != nil || mode != \"wal\" { return fmt.Errorf(\"db not in WAL mode: %q\", mode) }\n","typeGuard":null,"tryCatchPattern":"var mode string\nif err := db.db.QueryRowContext(ctx, `PRAGMA journal_mode = wal;`).Scan(&mode); err != nil {\n    return err\n} else if mode != \"wal\" {\n    return fmt.Errorf(\"enable wal failed, mode=%q\", mode) // handle by fixing fs/permissions\n}\n","preventionTips":["Keep the database on a local filesystem that supports WAL, not NFS/SMB","Ensure the db directory is writable so -wal and -shm files can be created","Set journal_mode=wal persistently with all other connections closed","Verify read-only mounts/flags (immutable=1 DSN flags, ro filesystems) are not in play"],"tags":["sqlite","wal","pragma","journal-mode"],"backgroundTag":"invalid-state-transition","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"}