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

  1. Read the wrapped errno to identify the exact stat failure.
  2. Check execute (x) permission on every parent directory of the db path for the litestream user.
  3. Verify the db path components: no symlink loops, no file-where-directory-expected.
  4. 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

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


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