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

  1. 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
  2. Remove the existing files manually (`rm /path/to/db /path/to/db-wal /path/to/db-shm /path/to/db-journal`) then rerun restore
  3. Check for an immutable flag (`lsattr`, `chattr -i`) or a read-only mount (`mount | grep ro`) and correct it
  4. 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

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


AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06). Data as JSON: /api/errors/9c645ce0db787b00. Report an issue: GitHub.