{"record":{"id":"4dc26c5deef3cf37","repo":"benbjohnson/litestream","slug":"rename-hydration-meta-w","errorCode":null,"errorMessage":"rename hydration meta: %w","messagePattern":"rename hydration meta: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"vfs.go","lineNumber":1021,"sourceCode":"\t\tif closeErr := tmp.Close(); closeErr != nil {\n\t\t\th.logger.Warn(\"failed to close temp meta file during cleanup\", \"error\", closeErr)\n\t\t}\n\t\tif removeErr := os.Remove(tmpPath); removeErr != nil {\n\t\t\th.logger.Warn(\"failed to remove temp meta file during cleanup\", \"error\", removeErr)\n\t\t}\n\t\treturn fmt.Errorf(\"sync temp meta file: %w\", err)\n\t}\n\tif err := tmp.Close(); err != nil {\n\t\tif removeErr := os.Remove(tmpPath); removeErr != nil {\n\t\t\th.logger.Warn(\"failed to remove temp meta file during cleanup\", \"error\", removeErr)\n\t\t}\n\t\treturn fmt.Errorf(\"close temp meta file: %w\", err)\n\t}\n\tif err := os.Rename(tmpPath, h.metaPath()); err != nil {\n\t\tif removeErr := os.Remove(tmpPath); removeErr != nil {\n\t\t\th.logger.Warn(\"failed to remove temp meta file during cleanup\", \"error\", removeErr)\n\t\t}\n\t\treturn fmt.Errorf(\"rename hydration meta: %w\", err)\n\t}\n\tif err := syncDir(filepath.Dir(h.metaPath())); err != nil {\n\t\treturn fmt.Errorf(\"sync hydration meta directory: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc syncDir(path string) error {\n\tdir, err := os.Open(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer dir.Close()\n\treturn dir.Sync()\n}\n\nfunc NewVFSFile(client ReplicaClient, name string, logger *slog.Logger) *VFSFile {\n\tf := &VFSFile{","sourceCodeStart":1003,"sourceCodeEnd":1039,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/vfs.go#L1003-L1039","documentation":"Hydrator.saveMeta wraps os.Rename failures as \"rename hydration meta\". The atomic temp-file-to-meta rename is the commit point for the saved TXID; failure leaves no updated meta, so the next open falls back to full hydration.","triggerScenarios":"Rename across a filesystem boundary is not the case here (same directory), but the target directory may have become read-only or been removed; EXDEV-style errors from overlay/network filesystems; target meta path locked by another process on filesystems with mandatory locking; ENOSPC on directory metadata update.","commonSituations":"OverlayFS/container volume quirks; the hydration directory deleted mid-run; SELinux/AppArmor denying the rename operation.","solutions":["Verify the hydration directory still exists and is writable (ls -ld)","Check container/SELinux policies denying rename in the hydration directory","Avoid network/overlay filesystems for hydration state; use local disk","Non-fatal for replication: restart litestream after fixing the filesystem to re-hydrate"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// verify the target path is renameable: same-dir, writable parent\ndir := filepath.Dir(hydrationPath)\nif fi, err := os.Stat(dir); err != nil || !fi.IsDir() {\n\t// recreate the directory before opening litestream\n}","typeGuard":null,"tryCatchPattern":"if err := saveMeta(); err != nil {\n\tvar linkErr *os.LinkError\n\tif errors.As(err, &linkErr) {\n\t\t// inspect linkErr.Err (EACCES, ENOENT, EXDEV) and fix filesystem\n\t}\n}","preventionTips":["Keep hydration state on simple local filesystems, not overlay/network mounts","Audit SELinux/AppArmor policies for write+rename in the data directory","Ensure the hydration directory is not removed by cleanup jobs at runtime","Know the blast radius: only resume efficiency is lost; the replica remains the source of truth"],"tags":["filesystem","rename","hydration"],"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-14T05:17:10.506Z"}