{"record":{"id":"be0ae35ec04abc73","repo":"benbjohnson/litestream","slug":"sync-ltx-dir-w","errorCode":null,"errorMessage":"sync ltx dir: %w","messagePattern":"sync ltx dir: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"db.go","lineNumber":2244,"sourceCode":"\n\t// Atomically rename file to final path.\n\tdb.setSyncDiagPhase(diagPhaseRenameLTX, func(s *diagState) {\n\t\ts.txID = txID\n\t\ts.walSize = sz\n\t})\n\tif err := os.Rename(tmpFilename, filename); err != nil {\n\t\tdb.maxLTXFileInfos.Lock()\n\t\tdelete(db.maxLTXFileInfos.m, 0) // clear cache if in unknown state\n\t\tdb.maxLTXFileInfos.Unlock()\n\t\tdb.invalidatePosCache()\n\t\treturn result, fmt.Errorf(\"rename ltx file: %w\", err)\n\t}\n\tif err := internal.FsyncDir(filepath.Dir(filename)); err != nil {\n\t\tdb.maxLTXFileInfos.Lock()\n\t\tdelete(db.maxLTXFileInfos.m, 0) // clear cache if in unknown state\n\t\tdb.maxLTXFileInfos.Unlock()\n\t\tdb.invalidatePosCache()\n\t\treturn result, fmt.Errorf(\"sync ltx dir: %w\", err)\n\t}\n\n\tresult.synced = true\n\tresult.l0FileInfo = &ltx.FileInfo{\n\t\tLevel:     0,\n\t\tMinTXID:   txID,\n\t\tMaxTXID:   txID,\n\t\tCreatedAt: time.Now(),\n\t\tSize:      enc.N(),\n\t}\n\n\tencPos := enc.PostApplyPos()\n\tresult.pos = &encPos\n\n\t// Track the logical end of WAL content for checkpoint decisions.\n\t// This is the WALOffset + WALSize from the LTX we just created.\n\t// Using this instead of file size prevents issue #997 where stale\n\t// frames with old salt values cause perpetual checkpoint triggering.","sourceCodeStart":2226,"sourceCodeEnd":2262,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/db.go#L2226-L2262","documentation":"After renaming the LTX file, litestream fsyncs the parent directory so the rename is durable across crashes. A failure here is wrapped as 'sync ltx dir:'; like the rename failure, it clears cached file-info and position state because durability of the new LTX file is not guaranteed.","triggerScenarios":"internal.FsyncDir(filepath.Dir(filename)) at db.go:2239 returned an error: the directory fd could not be opened (permissions, deleted dir), or fsync on directories is unsupported by the filesystem (some NFS/FUSE mounts, certain Windows configurations).","commonSituations":"Data directory on a network or FUSE filesystem that rejects directory fsync; the LTX directory removed concurrently by retention cleanup; permission changes preventing opening the directory.","solutions":["Confirm the LTX directory exists and is openable by the process at failure time.","If the filesystem does not support directory fsync, relocate the data directory to a POSIX-compliant local filesystem.","Retry the sync; if the rename actually persisted, the next successful sync repairs cached state.","Investigate cleanup jobs that might delete the LTX directory during active syncs."],"exampleFix":"// before\npath: /mnt/efs/db   # NFS: dir fsync unsupported/erratic\n// after\npath: /var/lib/litestream/db  # local ext4/xfs","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func supportsDirFsync(dir string) error {\n\treturn internal.FsyncDir(dir) // pre-flight in setup\n}","tryCatchPattern":"if err := db.Sync(ctx); err != nil {\n\tif strings.Contains(err.Error(), \"sync ltx dir\") {\n\t\t// verify dir fsync support; consider relocating storage\n\t}\n\treturn err\n}","preventionTips":["Run litestream on ext4/xfs or another directory-fsync-capable FS.","Test FsyncDir on your mount during deployment.","Prevent cleanup jobs from deleting the LTX directory mid-sync.","Verify directory permissions for the litestream user."],"tags":["fsync","durability","filesystem","directory"],"backgroundTag":"file-write-failed","analyzedSha":"4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3","analyzedAt":"2026-09-06T18:29:25.564Z","contentChangedAt":"2026-09-06T18:29:25.564Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}