benbjohnson/litestream · error
write snapshot ltx: %w
Error message
write snapshot ltx: %w
What it means
Wraps a failure from db.writeLTXFromDB, the step that streams every page of the live database into the LTX snapshot encoder (applying WAL page overrides from the page map). Since LTX files are immutable and a snapshot must be complete, any read error from the database file or write error into the encoder aborts the snapshot stream. Delivered via pw.CloseWithError so the consumer sees 'write snapshot ltx: ...'.
Source
Thrown at db.go:2885
if err := enc.EncodeHeader(ltx.Header{
Version: ltx.Version,
Flags: ltx.HeaderFlagNoChecksum,
PageSize: uint32(pos.pageSize),
Commit: commit,
MinTXID: 1,
MaxTXID: pos.pos.TXID,
Timestamp: time.Now().UnixMilli(),
WALOffset: walOffset,
WALSize: walSize,
WALSalt1: rd.salt1,
WALSalt2: rd.salt2,
}); err != nil {
pw.CloseWithError(fmt.Errorf("encode ltx snapshot header: %w", err))
return
}
if err := db.writeLTXFromDB(ctx, enc, walFile, commit, pageMap); err != nil {
pw.CloseWithError(fmt.Errorf("write snapshot ltx: %w", err))
return
}
if err := enc.Close(); err != nil {
pw.CloseWithError(fmt.Errorf("close ltx snapshot encoder: %w", err))
return
}
_ = pw.Close()
}()
return &snapshotReadCloser{PipeReader: pr, pos: pos}, nil
}
func snapshotHeaderWALRange(maxOffset, frameSize int64) (offset, size int64) {
if maxOffset <= WALHeaderSize || frameSize <= 0 {
return WALHeaderSize, 0
}
offset = max(maxOffset-frameSize, WALHeaderSize)View on GitHub (pinned to 4ed7a308f6)
Solutions
- Look at the wrapped inner error to distinguish db-read failures from pipe/cancellation failures
- Verify the database file is readable and on healthy storage for the whole snapshot duration; retry after fixing
- Increase consumer timeout / avoid cancelling context mid-snapshot for large databases
- Prevent external processes from moving/truncating the db during replication; keep a single replicator per DB
Defensive patterns
Strategy: retry
Validate before calling
// preflight db readability for the whole file
f, err := os.Open(dbPath)
if err != nil { return err }
if _, err := io.ReadFull(f, make([]byte, 100)); err != nil { return fmt.Errorf("db unreadable: %w", err) }
f.Close() Try / catch
err := streamSnapshot(ctx, db)
var perr *fs.PathError
switch {
case errors.As(err, &perr):
// repair path/permissions, then retry
case errors.Is(err, context.Canceled):
// consumer timeout; raise timeout and retry
default:
return retrySnapshot(ctx, pos)
} Prevention
- Ensure disk health and sufficient space for the db volume
- Avoid NFS for live databases; use local storage
- Set timeouts proportional to database size (>1GB needs longer)
- Do not shrink/move the database during replication
When it happens
Trigger: writeLTXFromDB(ctx, enc, walFile, commit, pageMap) errors: an I/O error reading a database page from disk, a context cancellation mid-stream, or a pipe write failure because the consumer closed early; page-count/page-size reads on the live DB can also fail if the file shrinks or moves.
Common situations: Database file unreadable mid-snapshot (permissions, deleted file, NFS stall); disk I/O errors; consumer timeout/cancellation while streaming a large (>1GB) database; SQLite checkpointing concurrently altering the db size mid-read.
Related errors
- encode ltx snapshot header: %w
- close ltx snapshot encoder: %w
- snapshot interval must be greater than 0
- write ltx from db: %w
- encode ltx frame (pgno=%d): %w
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/17958d10e5edd49f.
Report an issue: GitHub.