benbjohnson/litestream · error
open db: %w
Error message
open db: %w
What it means
Store.RegisterDB opens the database (db.Open) outside the store lock; any failure is wrapped as "open db: %w". The cause is whatever db.Open hit — typically the SQLite file is missing, unreadable, corrupt, or the WAL/SHM cannot be created. Because this is a disaster-recovery tool the open failure is returned to the caller rather than swallowed.
Source
Thrown at store.go:317
s.mu.Unlock()
return nil
}
}
s.mu.Unlock()
// Apply store-wide settings before opening the database.
db.SetLogger(s.Logger.With(LogKeyDB, filepath.Base(db.Path())))
db.L0Retention = s.L0Retention
db.ShutdownSyncTimeout = s.ShutdownSyncTimeout
db.ShutdownSyncInterval = s.ShutdownSyncInterval
db.VerifyCompaction = s.VerifyCompaction
db.RetentionEnabled = s.RetentionEnabled
db.Done = s.done
// Open the database without holding the lock to avoid blocking other operations.
// The double-check pattern below handles the race condition.
if err := db.Open(); err != nil {
return fmt.Errorf("open db: %w", err)
}
// Second check: verify database wasn't added by another goroutine while we were opening.
// If it was, close our instance and return without error.
s.mu.Lock()
for _, existing := range s.dbs {
if existing.Path() == db.Path() {
// Another goroutine added this database while we were opening.
// Release lock before closing to avoid potential deadlock.
s.mu.Unlock()
if err := db.Close(context.Background()); err != nil {
db.Logger.Error("close duplicate db", "path", db.Path(), "error", err)
}
return nil
}
}
View on GitHub (pinned to 4ed7a308f6)
Solutions
- Unwrap the error to see the underlying cause (os.PathError / SQLite error code)
- Verify the database file exists at the configured path and the process has read/write access to the db, -wal, and -shm files
- Ensure the application has created the database before registering it (or create it with sqlite3 first)
- Check disk space and volume mount status
Example fix
// before litestream.yml: dbpath: /data/app.db # file not created yet // after $ sqlite3 /data/app.db "PRAGMA journal_mode=WAL; SELECT 1;" $ # then start litestream
Defensive patterns
Strategy: fallback
Validate before calling
info, err := os.Stat(dbPath)
if err != nil { return err } // missing file
if info.IsDir() { return fmt.Errorf("%s is a directory", dbPath) }
if f, err := os.OpenFile(dbPath, os.O_RDWR, 0); err != nil { return err } else { f.Close() } Try / catch
if err := store.RegisterDB(db); err != nil {
if strings.HasPrefix(err.Error(), "open db:") {
// unwrap cause: missing file, permissions, or corruption; fix environment, don't blind-retry
log.Printf("db open failed: %v", err)
}
} Prevention
- Verify the SQLite file exists before starting litestream
- Run litestream under a user with read/write access to the db, -wal, and -shm files
- Confirm volume mounts and disk space in containers
- Create the database with WAL journal mode before registration
When it happens
Trigger: RegisterDB is called (directly or via handleRegister / handlePotentialDatabase) and db.Open fails: file does not exist, permission denied on db or its -wal/-shm files, SQLite file corruption, directory missing, or disk I/O error.
Common situations: Pointing litestream at a path where the application has not created the SQLite file yet; running litestream as a different user than the app; container volume not mounted; WAL file left unwritable after permission changes.
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
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/cddcc33343633a47.
Report an issue: GitHub.