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
- Place the output path on a local filesystem with full directory-fsync support (ext4, xfs)
- If using NFS/FUSE, accept the reduced crash-durability guarantee or move to local storage
- Check that the directory still exists and is writable at time of fsync
- 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
- Use ext4/xfs local storage for database and sidecar paths
- Treat NFS/FUSE output paths as reduced-durability environments
- Alert on this error: data is written but crash durability is weakened
- Document filesystem requirements in deployment configs
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.