benbjohnson/litestream · warning

sync txid dir: %w

Error message

sync txid dir: %w

What it means

This is the final durability step of WriteTXIDFile: after the atomic rename, the parent directory is fsynced (internal.FsyncDir) so the rename itself survives a crash. The error means fsync on the directory containing '<outputPath>-txid' failed. The TXID file content is written, but the rename may not be durable across a power loss.

Source

Thrown at replica.go:1748

	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).
func ReadTXIDFile(outputPath string) (ltx.TXID, error) {
	txidPath := TXIDPath(outputPath)

	data, err := os.ReadFile(txidPath)
	if os.IsNotExist(err) {
		return 0, nil
	} else if err != nil {
		return 0, fmt.Errorf("read txid file: %w", err)
	}

	txid, err := ltx.ParseTXID(strings.TrimSpace(string(data)))
	if err != nil {

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Place the output path on a local filesystem with full directory-fsync support (ext4, xfs)
  2. If using NFS/FUSE, accept the reduced crash-durability guarantee or move to local storage
  3. Check that the directory still exists and is writable at time of fsync
  4. Retry the operation; the data file itself was renamed successfully

Example fix

// before: assuming all filesystems support dir fsync
// (litestream handles this by returning the wrapped error)
// after: choose a local data directory in config
dbs:
  - path: /var/lib/litestream/mydb.db   # local ext4/xfs, not an NFS mount
Defensive patterns

Strategy: fallback

Validate before calling

// Probe directory-fsync support once at startup
if err := internal.FsyncDir(dataDir); err != nil {
    log.Printf("warning: dir fsync unsupported on %s: %v", dataDir, err)
}

Type guard

func dirFsyncSupported(dir string) bool {
    return internal.FsyncDir(dir) == nil
}

Try / catch

if err := WriteTXIDFile(outputPath, txid); err != nil {
    if isDirFsyncUnsupported(err) {
        log.Printf("warning: txid rename not durably synced: %v", err)
    } else { return err }
}

Prevention

When it happens

Trigger: Calling WriteTXIDFile on a filesystem where fsync on a directory descriptor fails — notably many network filesystems (NFS, CIFS), some FUSE filesystems, and certain container/overlay setups that reject directory fsync.

Common situations: Storing databases/replica output on NFS mounts; Docker volumes on filesystems without directory fsync support; read-only or failing mount for the output directory.

Related errors


AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06). Data as JSON: /api/errors/04772982c5706536. Report an issue: GitHub.