benbjohnson/litestream · error
frame salts until: %w
Error message
frame salts until: %w
What it means
WALReader.FrameSaltsUntil failed while scanning WAL frame salts to detect whether a FULL/RESTART checkpoint occurred. Litestream compares frame salts against its known salts; an I/O or parse error mid-scan aborts detection and is wrapped and returned to the sync loop.
Source
Thrown at db.go:1883
func (db *DB) detectFullCheckpoint(ctx context.Context, knownSalts [][2]uint32) (bool, error) {
walFile, err := os.Open(db.WALPath())
if err != nil {
return false, fmt.Errorf("open wal file: %w", err)
}
defer walFile.Close()
var lastKnownSalt [2]uint32
if len(knownSalts) > 0 {
lastKnownSalt = knownSalts[len(knownSalts)-1]
}
rd, err := NewWALReader(walFile, db.Logger.With(LogKeySubsystem, LogSubsystemWALReader))
if err != nil {
return false, fmt.Errorf("new wal reader: %w", err)
}
m, err := rd.FrameSaltsUntil(ctx, lastKnownSalt)
if err != nil {
return false, fmt.Errorf("frame salts until: %w", err)
}
// Remove known salts from the map.
for _, salt := range knownSalts {
delete(m, salt)
}
// If we have more than one unknown salt, then we have a FULL or RESTART checkpoint.
return len(m) >= 1, nil
}
type syncInfo struct {
offset int64 // end of the previous LTX read
salt1 uint32
salt2 uint32
prevCommit uint32
snapshotting bool // if true, a full snapshot is required
reason string // reason for snapshotView on GitHub (pinned to 4ed7a308f6)
Solutions
- Retry the sync — transient races with SQLite checkpointing usually resolve on the next iteration
- Check for context cancellation from shutdown and treat it as a normal stop, not an incident
- Run filesystem/disk health checks if errors persist
- Restart Litestream to rebuild known salts after a WAL restart (salts change on RESTART checkpoints)
Defensive patterns
Strategy: retry
Validate before calling
// no pre-call check possible; ensure ctx is live and WAL exists
select {
case <-ctx.Done():
return ctx.Err()
default:
} Try / catch
err := db.SyncAndWait(ctx)
if isCancellation(err) {
return nil // normal shutdown path
}
if err != nil {
scheduleRetryWithBackoff(err)
} Prevention
- Avoid killing Litestream mid-sync; use graceful shutdown so ctx cancels cleanly
- Monitor for repeated identical errors — persistent failures indicate disk issues
- Let Litestream own the WAL: don't run other tools that checkpoint aggressively
When it happens
Trigger: rd.FrameSaltsUntil(ctx, lastKnownSalt) errors: read failure partway through the WAL (partial frame, short read), context cancellation, or a frame that fails header validation.
Common situations: WAL file being rewritten concurrently by SQLite checkpoint while Litestream reads it; disk I/O errors on failing hardware; ctx cancelled during shutdown; WAL truncated between open and scan.
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
- set synchronous: %w
- checkpoint: %w
- checkpoint failed: %w
- sync database %s: %w
- enable wal failed, mode=%q
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/4327bef4f9598d45.
Report an issue: GitHub.