benbjohnson/litestream · error

sync txid file: %w

Error message

sync txid file: %w

What it means

WriteTXIDFile persists the replica's last-applied TXID to a sidecar '<db>-txid' file using a temp-file + fsync + rename protocol. This error wraps f.Sync(), meaning the OS failed to flush the temp file's data to stable storage. Litestream fsyncs before the atomic rename so the TXID survives a crash; a sync failure means durability cannot be guaranteed for the written TXID.

Source

Thrown at replica.go:1736

// WriteTXIDFile atomically writes a TXID to a sidecar file at <outputPath>-txid.
// Uses temp-file + fsync + rename for crash safety.
func WriteTXIDFile(outputPath string, txid ltx.TXID) error {
	txidPath := TXIDPath(outputPath)
	tmpPath := txidPath + ".tmp"

	f, err := os.Create(tmpPath)
	if err != nil {
		return fmt.Errorf("create txid temp file: %w", err)
	}
	defer f.Close()
	defer os.Remove(tmpPath)

	if _, err := fmt.Fprintln(f, txid); err != nil {
		return fmt.Errorf("write txid: %w", err)
	}

	if err := f.Sync(); err != nil {
		return fmt.Errorf("sync txid file: %w", err)
	}

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

	if err := os.Rename(tmpPath, txidPath); err != nil {
		return fmt.Errorf("rename txid file: %w", err)
	}

	if err := internal.FsyncDir(filepath.Dir(txidPath)); err != nil {
		return fmt.Errorf("sync txid dir: %w", err)
	}
	return nil
}

// ReadTXIDFile reads the TXID from a sidecar file at <outputPath>-txid.
// Returns 0, nil if the file does not exist (first run).

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Free disk space on the volume containing the output path and retry the operation
  2. Check dmesg / system logs for underlying device I/O errors and repair or replace the failing disk
  3. Verify the output path is on a local filesystem that supports fsync, not an unreliable network mount
  4. Delete any stale '<outputPath>-txid.tmp' file and rerun; the code removes the temp file on error via defer

Example fix

// before: silently ignoring the write error
if err := WriteTXIDFile(db.Path(), txid); err != nil { log.Println(err) }
// after: fail loudly so the caller can resync
if err := WriteTXIDFile(db.Path(), txid); err != nil {
    return fmt.Errorf("persist txid for %s: %w", db.Path(), err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Go: check writable space before writing
if st, err := os.Statfs(filepath.Dir(TXIDPath(outputPath))); err == nil && st.Bavail*uint64(st.Bsize) < 1<<20 {
    return fmt.Errorf("insufficient disk space for txid file")
}

Type guard

func canWriteDir(dir string) bool {
    f, err := os.CreateTemp(dir, ".probe")
    if err != nil { return false }
    f.Close(); os.Remove(f.Name())
    return true
}

Try / catch

if err := WriteTXIDFile(outputPath, txid); err != nil {
    if errors.Is(err, syscall.ENOSPC) {
        // free space / alert, then retry
    } else {
        return fmt.Errorf("persist txid: %w", err)
    }
}

Prevention

When it happens

Trigger: Calling WriteTXIDFile (directly or via replica restore/apply flows) when the filesystem rejects fsync: full disk on the volume holding the output path, I/O errors on the underlying device, or filesystems/containers where fsync is not supported on the temp file '<outputPath>-txid.tmp'.

Common situations: Disk-full on the host or in a Docker container after a large restore; NFS or network filesystems with unreliable fsync; failing disk reported through writeback errors; running in environments (some overlayfs setups) where fsync on a freshly created file errors.

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/89e6db1cd8205c01. Report an issue: GitHub.