{"record":{"id":"acb6f73800a4753e","repo":"benbjohnson/litestream","slug":"write-txid-w","errorCode":null,"errorMessage":"write txid: %w","messagePattern":"write txid: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"replica.go","lineNumber":1732,"sourceCode":"func TXIDPath(outputPath string) string {\n\treturn outputPath + \"-txid\"\n}\n\n// WriteTXIDFile atomically writes a TXID to a sidecar file at <outputPath>-txid.\n// Uses temp-file + fsync + rename for crash safety.\nfunc WriteTXIDFile(outputPath string, txid ltx.TXID) error {\n\ttxidPath := TXIDPath(outputPath)\n\ttmpPath := txidPath + \".tmp\"\n\n\tf, err := os.Create(tmpPath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"create txid temp file: %w\", err)\n\t}\n\tdefer f.Close()\n\tdefer os.Remove(tmpPath)\n\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","sourceCodeStart":1714,"sourceCodeEnd":1750,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/replica.go#L1714-L1750","documentation":"WriteTXIDFile wraps a failure writing the TXID line to the temporary file via fmt.Fprintln. This is rare (usually disk-full ENOSPC or an I/O error on the underlying file handle) but the TXID marker could not be persisted, so restore cannot safely record the last applied transaction.","triggerScenarios":"Disk filling up mid-write during restore; I/O errors on failing storage; filesystem quota exceeded while writing <output>.txid.tmp.","commonSituations":"DR recovery on an almost-full volume; network filesystems returning short writes/EIO; container ephemeral storage quota hit during a large restore.","solutions":["Free disk space or expand the volume, then re-run the restore.","Check dmesg/journal for I/O errors on the target device and replace failing media.","Restore to a healthy local path and sync to the final location afterwards.","Re-run the restore; the .tmp file is removed on failure so no partial state is left."],"exampleFix":"// before\n// error: write txid: write /data/db.sqlite.txid.tmp: no space left on device\n$ df -h /data   # 100% used\n// after\n$ rm /data/old-backups/*  # or expand volume\n$ litestream restore -o /data/db.sqlite db","handlingStrategy":"validation","validationCode":"// Go: check free space on the target filesystem before restore\nvar st syscall.Statfs_t\nif err := syscall.Statfs(dir, &st); err == nil {\n    avail := int64(st.Bavail) * int64(st.Bsize)\n    if avail < minRequiredBytes {\n        return fmt.Errorf(\"insufficient space: %d bytes free\", avail)\n    }\n}","typeGuard":null,"tryCatchPattern":"if err := WriteTXIDFile(path, txid); err != nil {\n    if strings.Contains(err.Error(), \"write txid\") {\n        // almost always ENOSPC or device I/O error: free space / check dmesg, then re-run\n    }\n    return err\n}","preventionTips":["Monitor disk usage and alert before volumes reach capacity.","Avoid restoring to flaky network filesystems; restore locally then sync.","Re-run restores after transient write failures: .tmp files are cleaned up automatically."],"tags":["restore","filesystem","file-write","disk-full"],"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"}