benbjohnson/litestream · error

sync L0 file

Error message

sync L0 file: %w

What it means

After copying, the temp file is flushed to disk with tmpFile.Sync() before rename. This error wraps a failure of fsync on the temp L0 file, meaning the fetched LTX data could not be durably committed to storage and the file is abandoned.

Solutions

  1. Free disk space (df -h) — ENOSPC at fsync time is common even when create/copy succeeded.
  2. Check dmesg/smart data for device I/O errors on the data volume.
  3. If running in a container, verify the storage driver supports reliable fsync (avoid tmpfs for the LTX dir).
  4. Retry after fixing the disk; the .tmp file is cleaned up automatically on error.

Example fix

// before: data volume at 100%
$ df -h /var/lib/db  # 100% used
// after: free space or grow volume before restarting litestream
$ systemctl stop litestream && rm -f /var/lib/db/*.tmp && systemctl start litestream
Defensive patterns

Strategy: try-catch

Validate before calling

// Keep headroom: fail early if disk is nearly full
st, _ := os.Statfs(dataDir)
if st.Bavail*uint64(st.Bsize) < 128*1024*1024 {
    log.Printf("refusing fetch, low disk")
}

Type guard

null

Try / catch

if err := tmpFile.Sync(); err != nil {
    _ = tmpFile.Close()
    return fmt.Errorf("sync L0 file: %w", err)
} // ENOSPC -> free space; EIO -> check dmesg for device errors

Prevention

When it happens

Trigger: checkDatabaseBehindReplica calls tmpFile.Sync() after a successful io.Copy and the OS returns an error — typically ENOSPC (no space left to flush dirty pages) or EIO (underlying device failure).

Common situations: Disk filling up between the copy and the sync; failing or failing-over storage hardware; container filesystems (some overlay/tmpfs setups) returning errors on fsync.

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/1fd0fa50fce6e3d9. Report an issue: GitHub.

Appendix: source

Thrown at db.go:1648

	// Write to temp file and atomically rename
	localPath := db.LTXPath(0, minTXID, maxTXID)
	tmpPath := localPath + ".tmp"

	tmpFile, err := os.Create(tmpPath)
	if err != nil {
		return fmt.Errorf("create temp L0 file: %w", err)
	}
	defer func() { _ = os.Remove(tmpPath) }() // Clean up temp file on error

	if _, err := io.Copy(tmpFile, reader); err != nil {
		_ = tmpFile.Close()
		return fmt.Errorf("copy L0 file: %w", err)
	}

	if err := tmpFile.Sync(); err != nil {
		_ = tmpFile.Close()
		return fmt.Errorf("sync L0 file: %w", err)
	}

	if err := tmpFile.Close(); err != nil {
		return fmt.Errorf("close L0 file: %w", err)
	}

	// Atomically rename temp file to final path
	if err := os.Rename(tmpPath, localPath); err != nil {
		return fmt.Errorf("rename L0 file: %w", err)
	}
	db.invalidatePosCache()

	db.Logger.Info("fetched latest L0 file from replica",
		"min_txid", minTXID,
		"max_txid", maxTXID)

	return nil
}

View on GitHub (pinned to 4ed7a308f6)