benbjohnson/litestream · error
rename L0 file: %w
Error message
rename L0 file: %w
What it means
The final step stages the fetched LTX file by atomically renaming tmpPath to the final LTX path within the L0 directory. This error wraps os.Rename failing, meaning the fetched file could not be adopted even though it downloaded successfully.
Source
Thrown at db.go:1657
defer func() { _ = os.Remove(tmpPath) }() // Clean up temp file on error
if _, err := io.Copy(tmpFile, reader); err != nil {
_ = tmpFile.Close()
return fmt.Errorf("copy L0 file: %w", err)
}
if err := tmpFile.Sync(); err != nil {
_ = tmpFile.Close()
return fmt.Errorf("sync L0 file: %w", err)
}
if err := tmpFile.Close(); err != nil {
return fmt.Errorf("close L0 file: %w", err)
}
// Atomically rename temp file to final path
if err := os.Rename(tmpPath, localPath); err != nil {
return fmt.Errorf("rename L0 file: %w", err)
}
db.invalidatePosCache()
db.Logger.Info("fetched latest L0 file from replica",
"min_txid", minTXID,
"max_txid", maxTXID)
return nil
}
// verify ensures the current LTX state matches where it left off from
// the real WAL. Check info.ok if verification was successful.
func (db *DB) verify(ctx context.Context, state *syncState) (info syncInfo, err error) {
pos, err := db.Pos()
if err != nil {
return info, fmt.Errorf("pos: %w", err)
}
View on GitHub (pinned to 4ed7a308f6)
Solutions
- Ensure nothing else (cron jobs, antivirus, other litestream instances) touches the LTX directory while litestream runs.
- Check that localPath is not an existing directory (ls -ld) and remove bad state, or run `litestream reset` to rebuild local LTX state.
- Verify tmp and final paths are on the same filesystem (they should be — both derive from LTXPath in the same dir); fix bind-mount changes.
- Inspect the wrapped OS error for the exact path and errno, then let the next sync retry.
Example fix
// before: cron cleaner removes *.tmp mid-fetch */5 * * * * find /var/lib/db -name '*.tmp' -delete // after: exclude the litestream LTX dir from cleanup */5 * * * * find /var/lib/db -name '*.tmp' -not -path '*/ltx/*' -delete
Defensive patterns
Strategy: validation
Validate before calling
// Ensure no competing cleaner or stale target before fetch
if fi, err := os.Lstat(localPath); err == nil && fi.IsDir() {
log.Fatalf("target path is a directory: %s", localPath)
} Type guard
null
Try / catch
if err := os.Rename(tmpPath, localPath); err != nil {
return fmt.Errorf("rename L0 file: %w", err)
} // if persistent, `litestream reset` rebuilds local LTX state cleanly Prevention
- Exclude ltx/ trees from cron cleaners, antivirus, and sync tools
- Run a single litestream instance per database
- Keep temp and final paths on the same filesystem (default behavior)
- Use `litestream reset` for corrupt local state instead of manual surgery
When it happens
Trigger: checkDatabaseBehindReplica calls os.Rename(tmpPath, localPath) after a successful Close and the rename fails — the target path exists in a way rename can't replace (directory), the L0 dir permissions changed, the source .tmp was removed by an external cleaner, or the filesystem doesn't support atomic rename across the involved paths.
Common situations: A cleanup job (tmpwatch/cron) deleted the .tmp between close and rename; target path turned into a directory due to a prior corrupt state; different filesystems for temp and target paths after a bind-mount change; Windows/limited network filesystems with weak rename semantics.
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
- remove L0 directory: %w
- recreate L0 directory: %w
- create temp L0 file: %w
- close L0 file: %w
- rename to output path: %w
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/6bfd05a182f2778f.
Report an issue: GitHub.