benbjohnson/litestream · error
sync before truncate: %w
Error message
sync before truncate: %w
What it means
After all pages are written, applyLTXFile calls f.Sync() on the database file before truncating it to the committed size recorded in the LTX header. This error means that fsync failed, so Litestream refuses to truncate because durability of already-written pages is not guaranteed.
Source
Thrown at replica.go:987
break
} else if err != nil {
return fmt.Errorf("decode page: %w", err)
}
if phdr.Pgno == 1 && len(data) >= 28 {
data[18], data[19] = 0x01, 0x01
_, _ = rand.Read(data[24:28])
}
off := int64(phdr.Pgno-1) * int64(pageSize)
if _, err := f.WriteAt(data, off); err != nil {
return fmt.Errorf("write page %d: %w", phdr.Pgno, err)
}
}
if hdr.Commit > 0 {
if err := f.Sync(); err != nil {
return fmt.Errorf("sync before truncate: %w", err)
}
newSize := int64(hdr.Commit) * int64(pageSize)
if err := f.Truncate(newSize); err != nil {
return fmt.Errorf("truncate: %w", err)
}
}
if err := dec.Close(); err != nil {
return fmt.Errorf("close decoder: %w", err)
}
return f.Sync()
}
// fillFollowGap attempts to bridge a gap in level 0 files by searching
// higher compaction levels for a file that covers the missing TXID range.
func (r *Replica) fillFollowGap(ctx context.Context, f *os.File, afterTXID ltx.TXID, gapMinTXID ltx.TXID, pageSize uint32) (ltx.TXID, error) {
currentTXID := afterTXIDView on GitHub (pinned to 4ed7a308f6)
Solutions
- Check the health of the filesystem backing the database (dmesg/smartctl) and fix the I/O error
- Avoid applying LTX files on network filesystems that do not reliably support fsync; use local disk
- Ensure no other process deletes/locks the database file during replication
- Retry sync after the storage issue clears
Example fix
// before: applying to an NFS mount path = "/mnt/efs/db.sqlite" // after: use local persistent disk path = "/var/lib/litestream/db.sqlite"
Defensive patterns
Strategy: fallback
Validate before calling
// verify the filesystem supports reliable fsync before pointing litestream at it
if err := checkFsyncSupport(dbDir); err != nil {
log.Fatalf("filesystem %s does not support fsync reliably: %v", dbDir, err)
} Try / catch
if err := r.applyLTXFile(ctx, f, info, pageSize); err != nil {
if strings.Contains(err.Error(), "sync before truncate") {
return fmt.Errorf("fsync failed on %s; check volume health before retry: %w", dbPath, err)
}
return err
} Prevention
- Keep the SQLite database on local or reliably fsync-capable storage (avoid NFS/EFS/SMB)
- Prevent other processes from deleting or replacing the DB file while litestream runs
- Monitor storage hardware/volume errors
- After a sync failure, verify file state before retrying to avoid torn writes
When it happens
Trigger: f.Sync() returns an error when hdr.Commit > 0: underlying storage I/O error, file closed/invalidated descriptor, or filesystem that does not support fsync properly (some network filesystems).
Common situations: Storage backend flakiness (EFS/NFS/SMB mounts), disk errors detected mid-sync, file removed or descriptor invalidated by another process while applying.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/24012a44b002291d.
Report an issue: GitHub.