Hmbown/CodeWhale · error
failed to enable WAL for
Error message
failed to enable WAL for {}: SQLite retained journal mode {configured_mode} What it means
The state store requires SQLite databases to run in WAL (write-ahead logging) journal mode for concurrent readers. configure_connection issues PRAGMA journal_mode=WAL and checks the mode SQLite actually switched to; some conditions make SQLite silently keep the old mode (e.g. 'delete'), and the code then bails rather than continue with degraded durability.
Solutions
- Move the database file to a local filesystem (WAL needs shared memory, unavailable on NFS/SMB).
- Close other connections/processes holding the DB before opening, so the mode switch isn't blocked.
- Run PRAGMA journal_mode; manually to see the retained mode and diagnose.
- Ensure no explicit transaction is open on the connection when the pragma runs.
- Check the SQLite version supports WAL (>= 3.7.0).
Example fix
// before: db on network share
let db = StateDb::open("/mnt/nfs/codewhale/state.db")?; // fails WAL
// after: local data dir
let db = StateDb::open("~/.local/state/codewhale/state.db")?; Defensive patterns
Strategy: fallback
Validate before calling
// Detect unsuitable filesystems before opening the db let meta = std::fs::metadata(db_path.parent().unwrap())?; // e.g. reject known network mount points or check /proc/mounts for nfs/cifs
Type guard
fn is_local_path(p: &Path) -> bool {
p.starts_with("/home") || p.starts_with("/Users") || p.starts_with("/tmp")
} Try / catch
match StateDb::open(db_path) {
Ok(db) => db,
Err(e) if e.to_string().contains("retained journal mode") => {
eprintln!("WAL unavailable on {db_path:?}; use a local disk");
StateDb::open(local_fallback_path())?
}
Err(e) => return Err(e),
} Prevention
- Place SQLite databases on local filesystems, never NFS/SMB
- Ensure no other process holds the db open with an active transaction
- Check PRAGMA journal_mode after opening to detect drift early
- Keep SQLite >= 3.7.0
When it happens
Trigger: Opening/initializing the database when SQLite refuses or fails to switch journal_mode to WAL — commonly because another connection holds the database in a different mode, the file is on a network filesystem that doesn't support WAL shared memory, or a transaction is active on the connection.
Common situations: Database on NFS/SMB/network mounts (classic WAL failure); another process/connection with an open transaction or lock; copying a non-WAL db file over the live one mid-run; very old SQLite builds; read-only filesystem (pragma_update itself would fail first).
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- Another pet recorder is using this output.
- thread not found
- channel does not exist
- channel does not exist
- external agy credential file
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/25bd048866555b51.
Report an issue: GitHub.
Appendix: source
Thrown at crates/state/src/lib.rs:345
conn.busy_timeout(std::time::Duration::from_secs(5))
.with_context(|| format!("failed to set busy_timeout for {}", db_path.display()))?;
conn.pragma_update(None, "foreign_keys", "ON")
.with_context(|| format!("failed to enable foreign keys for {}", db_path.display()))?;
// WAL persists in the database header, so established stores need no
// write-like journal transition on every process start. Fresh or
// explicitly downgraded stores still transition once, and we verify
// SQLite accepted the requested mode instead of silently retaining the
// previous one (for example on an unsupported VFS).
let journal_mode: String = conn
.pragma_query_value(None, "journal_mode", |row| row.get(0))
.with_context(|| format!("failed to read journal mode for {}", db_path.display()))?;
if !journal_mode.eq_ignore_ascii_case("wal") {
let configured_mode: String = conn
.pragma_update_and_check(None, "journal_mode", "WAL", |row| row.get(0))
.with_context(|| format!("failed to enable WAL for {}", db_path.display()))?;
if !configured_mode.eq_ignore_ascii_case("wal") {
anyhow::bail!(
"failed to enable WAL for {}: SQLite retained journal mode {configured_mode}",
db_path.display()
);
}
}
Ok(())
}
/// Returns the filesystem path of the underlying SQLite database.
pub fn db_path(&self) -> &Path {
&self.db_path
}
fn conn(&self) -> Result<MutexGuard<'_, Connection>> {
// Poisoning means a panic mid-operation; any open transaction was
// rolled back when it dropped, but surface the condition rather than
// silently continuing on a connection whose state we can't vouch for.
self.connView on GitHub (pinned to 73e0f67d83)