benbjohnson/litestream · error
stat database: %w
Error message
stat database: %w
What it means
DB.EnsureExists(ctx) stats db.Path() to decide whether the local database file exists. If os.Stat fails with any error other than "not exist" (e.g. permission denied on a parent directory, I/O error, or path is not accessible), litestream wraps it as "stat database". It does NOT occur when the file is simply missing — that is the normal restore path.
Source
Thrown at db.go:744
}
return nil
}
// EnsureExists restores the database from the configured replica if the local
// database file does not exist. If no backup is available, it returns nil and
// a fresh database will be created on Open(). Must be called before Open().
func (db *DB) EnsureExists(ctx context.Context) error {
if db.Replica == nil {
return fmt.Errorf("no replica configured")
}
if db.Replica.Client == nil {
return fmt.Errorf("no replica client configured")
}
if _, err := os.Stat(db.Path()); err == nil {
return nil
} else if !os.IsNotExist(err) {
return fmt.Errorf("stat database: %w", err)
}
if dir := filepath.Dir(db.Path()); dir != "." {
if err := os.MkdirAll(dir, 0o750); err != nil {
return fmt.Errorf("create parent directory: %w", err)
}
}
opt := NewRestoreOptions()
opt.OutputPath = db.Path()
opt.IntegrityCheck = IntegrityCheckQuick
if err := db.Replica.Restore(ctx, opt); err != nil {
if errors.Is(err, ErrTxNotAvailable) || errors.Is(err, ErrNoSnapshots) {
db.Logger.Debug("no backup found, will create fresh database")
return nil
}
return fmt.Errorf("restore from backup: %w", err)View on GitHub (pinned to 4ed7a308f6)
Solutions
- Read the wrapped errno to identify the exact stat failure.
- Check execute (x) permission on every parent directory of the db path for the litestream user.
- Verify the db path components: no symlink loops, no file-where-directory-expected.
- Fix ownership/permissions (chown/chmod) or adjust the db path in config; check LSM (SELinux/AppArmor) denials in audit logs.
Defensive patterns
Strategy: validation
Validate before calling
if _, err := os.Stat(db.Path()); err != nil && !os.IsNotExist(err) {
return fmt.Errorf("db path inaccessible before EnsureExists: %w", err)
} Try / catch
if err := db.EnsureExists(ctx); err != nil {
var pe *fs.PathError
if errors.As(err, &pe) && pe.Op == "stat" {
log.Error("cannot access db path", "path", pe.Path, "err", pe.Err)
}
return err
} Prevention
- Verify the litestream user has execute permission on all parent directories.
- Avoid symlink loops; use plain directories for data paths.
- Check SELinux/AppArmor policies when running under a hardened OS.
- Smoke-test os.Stat on the configured path in deployment checks.
When it happens
Trigger: os.Stat(db.Path()) returning e.g. EACCES, ELOOP, or ENOTDIR: a parent directory lacks execute/search permission, the path contains a bad symlink, or a path component is a file rather than a directory.
Common situations: Running litestream under a service user that cannot traverse /var/lib/appdata; a symlink loop after a bad migration; db path accidentally pointing inside a file (e.g. /data/db.sqlite/nested); SELinux/AppArmor blocking access.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- cannot access output path: %w
- cannot access SQLite sidecar path: %w
- remove existing output path: %w
- remove ltx directory: %w
- create parent directory: %w
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/c242f5824a708b28.
Report an issue: GitHub.