benbjohnson/litestream · error
rename txid file: %w
Error message
rename txid file: %w
What it means
After writing and fsyncing the temp file, WriteTXIDFile atomically renames '<outputPath>-txid.tmp' to '<outputPath>-txid'. This error means os.Rename failed, so the new TXID was never published; the previous TXID file (if any) remains untouched. Rename failures typically indicate a path-level problem rather than data corruption.
Source
Thrown at replica.go:1744
return fmt.Errorf("create txid temp file: %w", err)
}
defer f.Close()
defer os.Remove(tmpPath)
if _, err := fmt.Fprintln(f, txid); err != nil {
return fmt.Errorf("write txid: %w", err)
}
if err := f.Sync(); err != nil {
return fmt.Errorf("sync txid file: %w", err)
}
if err := f.Close(); err != nil {
return fmt.Errorf("close txid file: %w", err)
}
if err := os.Rename(tmpPath, txidPath); err != nil {
return fmt.Errorf("rename txid file: %w", err)
}
if err := internal.FsyncDir(filepath.Dir(txidPath)); err != nil {
return fmt.Errorf("sync txid dir: %w", err)
}
return nil
}
// ReadTXIDFile reads the TXID from a sidecar file at <outputPath>-txid.
// Returns 0, nil if the file does not exist (first run).
func ReadTXIDFile(outputPath string) (ltx.TXID, error) {
txidPath := TXIDPath(outputPath)
data, err := os.ReadFile(txidPath)
if os.IsNotExist(err) {
return 0, nil
} else if err != nil {
return 0, fmt.Errorf("read txid file: %w", err)View on GitHub (pinned to 4ed7a308f6)
Solutions
- Verify the directory of the output path exists and is writable by the litestream process
- Ensure the temp file and target are on the same filesystem (rename cannot cross devices) — keep outputPath on one volume
- Check permissions and path length for the '<outputPath>-txid' location
- Re-run restore/replication; the temp file is cleaned up by defer so a retry starts clean
Example fix
// before: writing temp file in a different filesystem than target tmpPath := "/tmp/mydb-txid.tmp" // then rename to /var/lib/db/mydb-txid // after tmpPath := finalPath + ".tmp" // same directory, same filesystem
Defensive patterns
Strategy: validation
Validate before calling
dir := filepath.Dir(TXIDPath(outputPath))
if st, err := os.Stat(dir); err != nil || !st.IsDir() {
return fmt.Errorf("output dir missing: %s", dir)
} Type guard
func renameable(tmp, dst string) bool {
return filepath.Dir(tmp) == filepath.Dir(dst) // same fs required
} Try / catch
if err := WriteTXIDFile(outputPath, txid); err != nil {
if errors.Is(err, syscall.EXDEV) || errors.Is(err, syscall.ENOENT) {
// recreate dir / keep temp file next to target, then retry
}
return err
} Prevention
- Always create the temp file in the same directory as the target
- Do not delete or recreate the database output directory while litestream runs
- Keep paths under filesystem path-length limits
- Grant write permission on the output directory to the litestream user
When it happens
Trigger: os.Rename(tmpPath, txidPath) failing because the target directory was removed mid-operation, the output path crosses filesystem boundaries (rename EXDEV), permission denied on the directory, or the target path is invalid/too long.
Common situations: Output directory deleted or recreated while litestream is running; output path pointing into a mounted volume where temp file and target end up on different devices; read-only or permission-restricted restore directories; restore path with a very long name.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
- rename L0 file: %w
- rename ltx file: %w
- failed to scan directory %s: %w
- failed to calculate relative path for %s: %w
- get database position: %w
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/a77144734eef0925.
Report an issue: GitHub.