benbjohnson/litestream · error
open db file descriptor: %w
Error message
open db file descriptor: %w
What it means
After enabling PERSIST_WAL, litestream opens a long-lived file descriptor on the database file with os.Open; this descriptor is required for non-OFD (POSIX flock-style) locking during sync. If the OS refuses to open the file, init fails with this wrapped error. Since litestream only initializes existing databases (it returns early if the file does not exist), failure usually means a filesystem-level problem rather than a missing file.
Source
Thrown at db.go:1060
return err
}
db.dirInfo = fi
dsn := fmt.Sprintf("file:%s?_pragma=busy_timeout(%d)&_pragma=wal_autocheckpoint(0)",
db.path, db.BusyTimeout.Milliseconds())
if db.db, err = sql.Open("sqlite", dsn); err != nil {
return err
}
// Set PERSIST_WAL to prevent WAL file removal when database connections close.
if err := db.setPersistWAL(ctx); err != nil {
return fmt.Errorf("set PERSIST_WAL: %w", err)
}
// Open long-running database file descriptor. Required for non-OFD locks.
if db.f, err = os.Open(db.path); err != nil {
return fmt.Errorf("open db file descriptor: %w", err)
}
// Ensure database is closed if init fails.
// Initialization can retry on next sync.
defer func() {
if err != nil {
_ = db.releaseReadLock()
db.db.Close()
db.f.Close()
db.db, db.f = nil, nil
}
}()
// Enable WAL and ensure it is set. New mode should be returned on success:
// https://www.sqlite.org/pragma.html#pragma_journal_mode
var mode string
if err := db.db.QueryRowContext(ctx, `PRAGMA journal_mode = wal;`).Scan(&mode); err != nil {
return errView on GitHub (pinned to 4ed7a308f6)
Solutions
- Check file permissions and the litestream process user: `ls -l /path/to/db` and ensure read access (chmod/chown or run litestream with the right user)
- Check the open-file limit: `ulimit -n` (or the container's nofile rlimit) and raise it if many databases are monitored
- Confirm the file was not deleted/replaced between startup steps; point litestream at a stable path
- Inspect dmesg/audit logs for SELinux or AppArmor denials if permissions look correct
Example fix
// before: run container as arbitrary user against root-owned db // after: grant access // chown litestream:litestream /var/lib/app/app.db // # or in Dockerfile // USER litestream
Defensive patterns
Strategy: validation
Validate before calling
fi, err := os.Stat(dbPath)
if err != nil { return err }
if fi.IsDir() { return errors.New("path is a directory") }
f, err := os.OpenFile(dbPath, os.O_RDONLY, 0)
if err != nil { return fmt.Errorf("pre-flight open failed: %w", err) }
f.Close()
Try / catch
if err := db.init(ctx); err != nil {
var pe *fs.PathError
if errors.As(err, &pe) && errors.Is(pe.Err, syscall.EMFILE) {
// raise RLIMIT_NOFILE and retry
}
log.Printf("init failed: %v", err)
}
Prevention
- Run litestream as a user with read access to the database file
- Raise the open-file limit (ulimit -n / container nofile) when monitoring many databases
- Don't delete or rotate the live database file while litestream is starting
- Check SELinux/AppArmor policies allow the litestream binary to read the db path
When it happens
Trigger: DB.init on an existing database file: os.Open(db.path) returns an error — permission denied on the file, a race where the file is unlinked between the earlier os.Stat and os.Open, too many open file descriptors (EMFILE), or the path is a directory/unreadable special file.
Common situations: Running litestream as a user without read access to the SQLite file (e.g. container running as non-root against a root-owned db); ulimit -n too low in containers spawning many databases; the database file being rotated/deleted by another process during startup; SELinux/AppArmor blocking file access.
Understand the failure class
Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.
Related errors
- cannot access output path: %w
- cannot access SQLite sidecar path: %w
- remove existing output path: %w
- remove ltx directory: %w
- stat database: %w
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/d152684182b8d08d.
Report an issue: GitHub.