{"record":{"id":"24012a44b002291d","repo":"benbjohnson/litestream","slug":"sync-before-truncate-w","errorCode":null,"errorMessage":"sync before truncate: %w","messagePattern":"sync before truncate: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"replica.go","lineNumber":987,"sourceCode":"\t\t\tbreak\n\t\t} else if err != nil {\n\t\t\treturn fmt.Errorf(\"decode page: %w\", err)\n\t\t}\n\n\t\tif phdr.Pgno == 1 && len(data) >= 28 {\n\t\t\tdata[18], data[19] = 0x01, 0x01\n\t\t\t_, _ = rand.Read(data[24:28])\n\t\t}\n\n\t\toff := int64(phdr.Pgno-1) * int64(pageSize)\n\t\tif _, err := f.WriteAt(data, off); err != nil {\n\t\t\treturn fmt.Errorf(\"write page %d: %w\", phdr.Pgno, err)\n\t\t}\n\t}\n\n\tif hdr.Commit > 0 {\n\t\tif err := f.Sync(); err != nil {\n\t\t\treturn fmt.Errorf(\"sync before truncate: %w\", err)\n\t\t}\n\t\tnewSize := int64(hdr.Commit) * int64(pageSize)\n\t\tif err := f.Truncate(newSize); err != nil {\n\t\t\treturn fmt.Errorf(\"truncate: %w\", err)\n\t\t}\n\t}\n\n\tif err := dec.Close(); err != nil {\n\t\treturn fmt.Errorf(\"close decoder: %w\", err)\n\t}\n\n\treturn f.Sync()\n}\n\n// fillFollowGap attempts to bridge a gap in level 0 files by searching\n// higher compaction levels for a file that covers the missing TXID range.\nfunc (r *Replica) fillFollowGap(ctx context.Context, f *os.File, afterTXID ltx.TXID, gapMinTXID ltx.TXID, pageSize uint32) (ltx.TXID, error) {\n\tcurrentTXID := afterTXID","sourceCodeStart":969,"sourceCodeEnd":1005,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/replica.go#L969-L1005","documentation":"After all pages are written, applyLTXFile calls f.Sync() on the database file before truncating it to the committed size recorded in the LTX header. This error means that fsync failed, so Litestream refuses to truncate because durability of already-written pages is not guaranteed.","triggerScenarios":"f.Sync() returns an error when hdr.Commit > 0: underlying storage I/O error, file closed/invalidated descriptor, or filesystem that does not support fsync properly (some network filesystems).","commonSituations":"Storage backend flakiness (EFS/NFS/SMB mounts), disk errors detected mid-sync, file removed or descriptor invalidated by another process while applying.","solutions":["Check the health of the filesystem backing the database (dmesg/smartctl) and fix the I/O error","Avoid applying LTX files on network filesystems that do not reliably support fsync; use local disk","Ensure no other process deletes/locks the database file during replication","Retry sync after the storage issue clears"],"exampleFix":"// before: applying to an NFS mount\npath = \"/mnt/efs/db.sqlite\"\n// after: use local persistent disk\npath = \"/var/lib/litestream/db.sqlite\"","handlingStrategy":"fallback","validationCode":"// verify the filesystem supports reliable fsync before pointing litestream at it\nif err := checkFsyncSupport(dbDir); err != nil {\n    log.Fatalf(\"filesystem %s does not support fsync reliably: %v\", dbDir, err)\n}","typeGuard":null,"tryCatchPattern":"if err := r.applyLTXFile(ctx, f, info, pageSize); err != nil {\n    if strings.Contains(err.Error(), \"sync before truncate\") {\n        return fmt.Errorf(\"fsync failed on %s; check volume health before retry: %w\", dbPath, err)\n    }\n    return err\n}","preventionTips":["Keep the SQLite database on local or reliably fsync-capable storage (avoid NFS/EFS/SMB)","Prevent other processes from deleting or replacing the DB file while litestream runs","Monitor storage hardware/volume errors","After a sync failure, verify file state before retrying to avoid torn writes"],"tags":["filesystem","fsync","durability","io"],"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"}