benbjohnson/litestream · error
cannot access SQLite sidecar path: %w
Error message
cannot access SQLite sidecar path: %w
What it means
prepareOutputPath in the litestream restore command verifies that the restore target path and its SQLite sidecar files (-wal, -shm, -journal) can be safely written. For each sidecar path it calls os.Stat; if stat fails with an error other than NotExist (e.g. permission denied on the parent directory, or the path component is not a directory), it wraps and returns that error. This is a fail-fast guard so restore never silently proceeds into a target it cannot inspect.
Source
Thrown at cmd/litestream/restore.go:287
info, err := os.Stat(path)
if os.IsNotExist(err) {
return nil
} else if err != nil {
return fmt.Errorf("cannot access output path: %w", err)
}
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),
}View on GitHub (pinned to 4ed7a308f6)
Solutions
- Check permissions on the output directory: run `ls -la` on the target path's parent and ensure the litestream process user can read/execute it
- Verify every component of the target path is a directory: `stat` each component of the path
- If the path is on a network mount, verify the mount is healthy (`df`, remount if stale)
- Restore to a different writable path and move the file into place afterwards
Example fix
// before litestream restore -o /var/lib/app/db.sqlite mydb // error: cannot access SQLite sidecar path: stat /var/lib/app/db.sqlite-wal: permission denied // after — fix directory ownership first sudo chown litestream:litestream /var/lib/app litestream restore -o /var/lib/app/db.sqlite mydb
Defensive patterns
Strategy: validation
Validate before calling
for _, suffix := range []string{"-wal", "-shm", "-journal"} {
if _, err := os.Stat(outputPath + suffix); err != nil && !os.IsNotExist(err) {
// abort: cannot inspect target
}
}
if fi, err := os.Stat(filepath.Dir(outputPath)); err != nil || !fi.IsDir() {
// abort: output directory missing or not a directory
} Prevention
- Pre-create the output directory with correct ownership before restoring
- Run litestream as the same user that owns the target directory
- Avoid restoring onto network or read-only filesystems
- Stat-check the target path and sidecars in a pre-flight script
When it happens
Trigger: Running `litestream restore` when os.Stat on <db>-wal, <db>-shm, or <db>-journal returns a non-NotExist error: the parent directory lacks read/execute permission, a path component is a file not a directory, or an I/O error occurs while resolving the path (e.g. stale NFS mount, broken symlink loop).
Common situations: Restoring into a directory owned by another user or with restrictive permissions (common in Docker containers running as non-root); restoring to a path whose parent was replaced by a regular file; network filesystems returning transient EIO/ESTALE on stat.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- remove existing output 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/7577505a490b7dad.
Report an issue: GitHub.