{"record":{"id":"256a1c0be21b5d17","repo":"benbjohnson/litestream","slug":"filecontrolpersistwal-w","errorCode":null,"errorMessage":"FileControlPersistWAL: %w","messagePattern":"FileControlPersistWAL: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"db.go","lineNumber":1016,"sourceCode":"\n// setPersistWAL sets the PERSIST_WAL file control on the database connection.\n// This prevents SQLite from removing the WAL file when connections close.\nfunc (db *DB) setPersistWAL(ctx context.Context) error {\n\tconn, err := db.db.Conn(ctx)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"get connection: %w\", err)\n\t}\n\tdefer conn.Close()\n\n\treturn conn.Raw(func(driverConn interface{}) error {\n\t\tfc, ok := driverConn.(sqlite.FileControl)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"driver does not implement FileControl\")\n\t\t}\n\n\t\t_, err := fc.FileControlPersistWAL(\"main\", 1)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"FileControlPersistWAL: %w\", err)\n\t\t}\n\n\t\treturn nil\n\t})\n}\n\n// init initializes the connection to the database.\n// Skipped if already initialized or if the database file does not exist.\nfunc (db *DB) init(ctx context.Context) (err error) {\n\t// Exit if already initialized.\n\tif db.db != nil {\n\t\treturn nil\n\t}\n\n\t// Exit if no database file exists.\n\tfi, err := os.Stat(db.path)\n\tif os.IsNotExist(err) {\n\t\treturn nil","sourceCodeStart":998,"sourceCodeEnd":1034,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/db.go#L998-L1034","documentation":"Litestream sets the PERSIST_WAL file control on the SQLite 'main' database so the -wal file survives connection close; without it, SQLite deletes the WAL and litestream cannot replicate. This error wraps a failure returned by fc.FileControlPersistWAL(\"main\", 1) itself — the type assertion already succeeded, but the driver/SQLite rejected the operation or returned SQLITE_NOTFOUND-style errors (e.g. the schema name is wrong or the operation is unsupported for the attached database).","triggerScenarios":"DB.init -> setPersistWAL -> conn.Raw: calling fc.FileControlPersistWAL(\"main\", 1) returns a non-nil error. Typical causes: the driver build lacks file-control support compiled in, SQLite returns SQLITE_NOTFOUND/SQLITE_ERROR for the requested op, or the database is in a state (e.g. already closed, corrupted header) where the file control cannot run.","commonSituations":"Operating on a database whose file is corrupt or unreadable mid-init; using a custom build of modernc.org/sqlite with file-control stubs; an unusual DSN causing the connection to attach something other than a normal main database; rare driver regressions after upgrading modernc.org/sqlite.","solutions":["Inspect the wrapped inner error (%w) to identify the SQLite result code and address that root cause first","Verify the database file opens cleanly outside litestream: `sqlite3 /path/to/db 'PRAGMA integrity_check;'` and that it can enter WAL mode","Upgrade modernc.org/sqlite to the latest release and rebuild litestream to rule out driver regressions","Retry litestream after ensuring no other process holds the database in a conflicting state (e.g. another tool mid-checkpoint)"],"exampleFix":"// diagnose the wrapped cause instead of the opaque wrapper\n_, err := fc.FileControlPersistWAL(\"main\", 1)\nif err != nil {\n    return fmt.Errorf(\"FileControlPersistWAL: %w\", err) // read the inner sqlite error\n}\n","handlingStrategy":"try-catch","validationCode":"// before init, check the db is healthy and writable:\n// sqlite3 /path/to/db 'PRAGMA integrity_check;'\n// sqlite3 /path/to/db 'PRAGMA journal_mode=wal;'\nif st, err := os.Stat(dbPath); err != nil || st.IsDir() { return err }\n","typeGuard":null,"tryCatchPattern":"if err := db.setPersistWAL(ctx); err != nil {\n    var inner string\n    fmt.Sscanf(err.Error(), \"FileControlPersistWAL: %s\", &inner) // inspect wrapped cause\n    log.Printf(\"persist-WAL setup failed: %v\", err)\n    // retry init on next sync\n}\n","preventionTips":["Keep modernc.org/sqlite up to date to avoid file-control regressions","Run integrity_check on the database before starting litestream","Ensure no conflicting processes hold locks during litestream startup","Log the fully unwrapped error chain (%v on the outer error preserves causes)"],"tags":["sqlite","file-control","persist-wal","driver"],"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"}