benbjohnson/litestream · error
write ltx from wal: %w
Error message
write ltx from wal: %w
What it means
db.writeLTXFromWAL failed with a non-disk-full error while copying WAL frames into the incremental LTX file. This is the incremental replication path: frames are read from the WAL file and encoded as page data; any read/encode failure other than ENOSPC aborts the sync for this transaction.
Source
Thrown at db.go:2193
if err := db.writeLTXFromDB(ctx, enc, walFile, commit, pageMap); err != nil {
if isDiskFullError(err) {
return result, NewLTXError("stage-write", tmpFilename, 0, uint64(txID), uint64(txID), fmt.Errorf("%w: %w", ErrDiskFull, err))
}
return result, fmt.Errorf("write ltx from db: %w", err)
}
} else {
db.setSyncDiagPhase(diagPhaseWriteLTXFromWAL,
func(s *diagState) {
s.txID = txID
s.walSize = sz
s.snapshotting = false
s.reason = info.reason
})
if err := db.writeLTXFromWAL(ctx, enc, walFile, info.prevCommit, commit, pageMap); err != nil {
if isDiskFullError(err) {
return result, NewLTXError("stage-write", tmpFilename, 0, uint64(txID), uint64(txID), fmt.Errorf("%w: %w", ErrDiskFull, err))
}
return result, fmt.Errorf("write ltx from wal: %w", err)
}
}
// Encode final trailer to the end of the LTX file.
db.setSyncDiagPhase(diagPhaseCloseLTX, func(s *diagState) {
s.txID = txID
s.walSize = sz
})
if err := enc.Close(); err != nil {
if isDiskFullError(err) {
return result, NewLTXError("stage-write", tmpFilename, 0, uint64(txID), uint64(txID), fmt.Errorf("%w: %w", ErrDiskFull, err))
}
return result, fmt.Errorf("close ltx encoder: %w", err)
}
// Sync & close LTX file.
db.setSyncDiagPhase(diagPhaseFsyncLTX, func(s *diagState) {
s.txID = txIDView on GitHub (pinned to 4ed7a308f6)
Solutions
- Verify no other process manipulates the database's WAL file; let litestream be the only WAL-aware monitor.
- Check WAL integrity (sqlite3 integrity_check) and confirm salt mismatch logs; a reset will resnapshot.
- Run `litestream reset` for the database to clear local LTX state and force a fresh snapshot.
- Check dmesg for storage read errors; retry after transient I/O issues clear.
- Make sure the app and litestream use compatible SQLite versions/builds (modernc.org/sqlite per repo guidance).
Example fix
// before: app cron checkpoints/truncates WAL externally */5 * * * * sqlite3 /data/app.db 'PRAGMA wal_checkpoint(TRUNCATE);' // after: remove the cron; rely on litestream-managed sync/checkpointing # (deleted cron entry)
Defensive patterns
Strategy: try-catch
Validate before calling
// ensure WAL exists and is not being rotated externally:
if _, err := os.Stat(dbPath + "-wal"); err != nil {
return fmt.Errorf("wal missing before sync: %w", err)
} Try / catch
if err != nil && strings.Contains(err.Error(), "write ltx from wal") {
log.Printf("incremental sync failed: %v; salts may have mismatched — consider litestream reset to resnapshot", err)
} Prevention
- Never checkpoint/truncate/delete the WAL outside litestream
- Let litestream be the sole WAL monitor for the database
- Use matched SQLite builds (modernc.org/sqlite) across the stack
- Reset and resnapshot after any WAL truncation or power-loss corruption
When it happens
Trigger: WAL file truncated, rotated, or replaced mid-read (salt/frame mismatch with rd.salt1/salt2 captured earlier); read I/O error on the WAL; context cancellation; ltx encoder write failure.
Common situations: Another process deleting/rotating the -wal file (e.g. running `sqlite3 db 'PRAGMA wal_checkpoint(TRUNCATE)'` or VACUUM concurrently via a different toolchain); mismatched sqlite library versions; crash-truncated WAL after power loss.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- encode ltx header: %w
- write ltx from db: %w
- close ltx encoder: %w
- decode
- prev WAL offset is less than the header size: %d
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/8604457e79785c08.
Report an issue: GitHub.