{"record":{"id":"9c645ce0db787b00","repo":"benbjohnson/litestream","slug":"remove-existing-output-path-w","errorCode":null,"errorMessage":"remove existing output path: %w","messagePattern":"remove existing output path: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/litestream/restore.go","lineNumber":293,"sourceCode":"\tif info.IsDir() {\n\t\treturn fmt.Errorf(\"cannot restore, output path is a directory: %s\", path)\n\t}\n\n\tif info.Size() > 0 && !force {\n\t\treturn fmt.Errorf(\"cannot restore, output path already exists and is not empty: %s. Use -force to overwrite\", path)\n\t}\n\n\tfor _, sidecarPath := range []string{path + \"-wal\", path + \"-shm\", path + \"-journal\"} {\n\t\tif _, err := os.Stat(sidecarPath); err == nil && !force {\n\t\t\treturn fmt.Errorf(\"cannot restore, SQLite sidecar path already exists: %s. Use -force to overwrite\", sidecarPath)\n\t\t} else if err != nil && !os.IsNotExist(err) {\n\t\t\treturn fmt.Errorf(\"cannot access SQLite sidecar path: %w\", err)\n\t\t}\n\t}\n\n\tfor _, removePath := range []string{path, path + \"-wal\", path + \"-shm\", path + \"-journal\"} {\n\t\tif err := os.Remove(removePath); err != nil && !os.IsNotExist(err) {\n\t\t\treturn fmt.Errorf(\"remove existing output path: %w\", err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// loadFromURL creates a replica & updates the restore options from a replica URL.\nfunc (c *RestoreCommand) loadFromURL(ctx context.Context, replicaURL string, ifDBNotExists bool, opt *litestream.RestoreOptions) (*litestream.Replica, error) {\n\tif opt.OutputPath == \"\" {\n\t\treturn nil, &usageError{\n\t\t\tmessage: \"-o is required when restoring from a replica URL\",\n\t\t\thint:    fmt.Sprintf(\"litestream restore -o /path/to/db %s\", replicaURL),\n\t\t}\n\t}\n\n\t// Exit successfully if the output file already exists.\n\tif _, err := os.Stat(opt.OutputPath); !os.IsNotExist(err) && ifDBNotExists {\n\t\treturn nil, errSkipDBExists\n\t}","sourceCodeStart":275,"sourceCodeEnd":311,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/cmd/litestream/restore.go#L275-L311","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nlitestream restore -o /var/lib/app/db.sqlite mydb\n// error: remove existing output path: remove /var/lib/app/db.sqlite-wal: permission denied\n\n// after — clean the target as the owning user first\nsudo 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\nlitestream restore -o /var/lib/app/db.sqlite mydb","handlingStrategy":"validation","validationCode":"if _, err := os.Stat(outputPath); err == nil {\n\tif err := os.Remove(outputPath); err != nil {\n\t\t// abort or prompt before invoking restore\n\t}\n}\n// also verify write permission on the directory\nif fi, err := os.Stat(filepath.Dir(outputPath)); err != nil || fi.Mode().Perm()&0200 == 0 { /* abort */ }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["filesystem","permissions","restore","sqlite"],"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"}