{"record":{"id":"04772982c5706536","repo":"benbjohnson/litestream","slug":"sync-txid-dir-w","errorCode":null,"errorMessage":"sync txid dir: %w","messagePattern":"sync txid dir: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"replica.go","lineNumber":1748,"sourceCode":"\n\tif _, err := fmt.Fprintln(f, txid); err != nil {\n\t\treturn fmt.Errorf(\"write txid: %w\", err)\n\t}\n\n\tif err := f.Sync(); err != nil {\n\t\treturn fmt.Errorf(\"sync txid file: %w\", err)\n\t}\n\n\tif err := f.Close(); err != nil {\n\t\treturn fmt.Errorf(\"close txid file: %w\", err)\n\t}\n\n\tif err := os.Rename(tmpPath, txidPath); err != nil {\n\t\treturn fmt.Errorf(\"rename txid file: %w\", err)\n\t}\n\n\tif err := internal.FsyncDir(filepath.Dir(txidPath)); err != nil {\n\t\treturn fmt.Errorf(\"sync txid dir: %w\", err)\n\t}\n\treturn nil\n}\n\n// ReadTXIDFile reads the TXID from a sidecar file at <outputPath>-txid.\n// Returns 0, nil if the file does not exist (first run).\nfunc ReadTXIDFile(outputPath string) (ltx.TXID, error) {\n\ttxidPath := TXIDPath(outputPath)\n\n\tdata, err := os.ReadFile(txidPath)\n\tif os.IsNotExist(err) {\n\t\treturn 0, nil\n\t} else if err != nil {\n\t\treturn 0, fmt.Errorf(\"read txid file: %w\", err)\n\t}\n\n\ttxid, err := ltx.ParseTXID(strings.TrimSpace(string(data)))\n\tif err != nil {","sourceCodeStart":1730,"sourceCodeEnd":1766,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/replica.go#L1730-L1766","documentation":"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.","triggerScenarios":"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.","commonSituations":"Storing databases/replica output on NFS mounts; Docker volumes on filesystems without directory fsync support; read-only or failing mount for the output directory.","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"],"exampleFix":"// before: assuming all filesystems support dir fsync\n// (litestream handles this by returning the wrapped error)\n// after: choose a local data directory in config\ndbs:\n  - path: /var/lib/litestream/mydb.db   # local ext4/xfs, not an NFS mount","handlingStrategy":"fallback","validationCode":"// Probe directory-fsync support once at startup\nif err := internal.FsyncDir(dataDir); err != nil {\n    log.Printf(\"warning: dir fsync unsupported on %s: %v\", dataDir, err)\n}","typeGuard":"func dirFsyncSupported(dir string) bool {\n    return internal.FsyncDir(dir) == nil\n}","tryCatchPattern":"if err := WriteTXIDFile(outputPath, txid); err != nil {\n    if isDirFsyncUnsupported(err) {\n        log.Printf(\"warning: txid rename not durably synced: %v\", err)\n    } else { return err }\n}","preventionTips":["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"],"tags":["filesystem","fsync","durability","litestream"],"backgroundTag":"fsync-directory-unsupported","analyzedSha":"4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3","analyzedAt":"2026-09-06T18:29:25.564Z","contentChangedAt":"2026-09-06T18:29:25.564Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}