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 := afterTXID

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Check the health of the filesystem backing the database (dmesg/smartctl) and fix the I/O error
  2. Avoid applying LTX files on network filesystems that do not reliably support fsync; use local disk
  3. Ensure no other process deletes/locks the database file during replication
  4. 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

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.