benbjohnson/litestream · error
remove existing output path: %w
Error message
remove existing output path: %w
What it means
After confirming the target is usable, prepareOutputPath deletes any pre-existing output database and its sidecar files (-wal, -shm, -journal) so the restore starts clean. If os.Remove fails with an error other than NotExist, the error is wrapped as 'remove existing output path'. This prevents restoring over a file that cannot be removed (e.g. locked or owned by another user), which would leave the database in a corrupt mixed state.
Source
Thrown at cmd/litestream/restore.go:293
if info.IsDir() {
return fmt.Errorf("cannot restore, output path is a directory: %s", path)
}
if info.Size() > 0 && !force {
return fmt.Errorf("cannot restore, output path already exists and is not empty: %s. Use -force to overwrite", path)
}
for _, sidecarPath := range []string{path + "-wal", path + "-shm", path + "-journal"} {
if _, err := os.Stat(sidecarPath); err == nil && !force {
return fmt.Errorf("cannot restore, SQLite sidecar path already exists: %s. Use -force to overwrite", sidecarPath)
} else if err != nil && !os.IsNotExist(err) {
return fmt.Errorf("cannot access SQLite sidecar path: %w", err)
}
}
for _, removePath := range []string{path, path + "-wal", path + "-shm", path + "-journal"} {
if err := os.Remove(removePath); err != nil && !os.IsNotExist(err) {
return fmt.Errorf("remove existing output path: %w", err)
}
}
return nil
}
// loadFromURL creates a replica & updates the restore options from a replica URL.
func (c *RestoreCommand) loadFromURL(ctx context.Context, replicaURL string, ifDBNotExists bool, opt *litestream.RestoreOptions) (*litestream.Replica, error) {
if opt.OutputPath == "" {
return nil, &usageError{
message: "-o is required when restoring from a replica URL",
hint: fmt.Sprintf("litestream restore -o /path/to/db %s", replicaURL),
}
}
// Exit successfully if the output file already exists.
if _, err := os.Stat(opt.OutputPath); !os.IsNotExist(err) && ifDBNotExists {
return nil, errSkipDBExists
}View on GitHub (pinned to 4ed7a308f6)
Solutions
- Ensure the process user has write permission on the target directory and owns the existing db/sidecar files: `ls -la` the path, `chown`/`chmod` as needed
- Remove the existing files manually (`rm /path/to/db /path/to/db-wal /path/to/db-shm /path/to/db-journal`) then rerun restore
- Check for an immutable flag (`lsattr`, `chattr -i`) or a read-only mount (`mount | grep ro`) and correct it
- Stop any process holding the files open (litestream itself or the app) before restoring
Example fix
// before litestream restore -o /var/lib/app/db.sqlite mydb // error: remove existing output path: remove /var/lib/app/db.sqlite-wal: permission denied // after — clean the target as the owning user first sudo rm -f /var/lib/app/db.sqlite /var/lib/app/db.sqlite-wal /var/lib/app/db.sqlite-shm /var/lib/app/db.sqlite-journal litestream restore -o /var/lib/app/db.sqlite mydb
Defensive patterns
Strategy: validation
Validate before calling
if _, err := os.Stat(outputPath); err == nil {
if err := os.Remove(outputPath); err != nil {
// abort or prompt before invoking restore
}
}
// also verify write permission on the directory
if fi, err := os.Stat(filepath.Dir(outputPath)); err != nil || fi.Mode().Perm()&0200 == 0 { /* abort */ } Prevention
- Remove or archive old database files before running restore
- Ensure the restore runs under the same user that owns existing db files
- Check lsattr for immutable flags on long-lived database files
- Never restore onto read-only mounts
When it happens
Trigger: Running `litestream restore` over an existing database file (with or without -force) when os.Remove on the db path or one of its sidecar files fails: the file is owned by another user, the directory is not writable, or the file is immutable (chattr +i) or held on a read-only filesystem.
Common situations: Running restore as a different user than the one who created the database (e.g. root-created files, service running as litestream); restoring onto a read-only mounted volume; SELinux/AppArmor denying unlink; immutable flag set on the old database file.
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
- cannot access SQLite sidecar path: %w
- cannot access output path: %w
- database does not exist: %w
- get initial size: %w
- get size after delete: %w
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/9c645ce0db787b00.
Report an issue: GitHub.