benbjohnson/litestream · warning
database already enabled: %s
Error message
database already enabled: %s
What it means
EnableDB refuses to start replication for a database whose DB object is already open (db.IsOpen() true). Enabling twice would double-start the monitor/replica loops, so the Store treats the second enable as an invalid state transition.
Source
Thrown at store.go:388
if err := db.Close(ctx); err != nil {
return fmt.Errorf("close db: %w", err)
}
return nil
}
// EnableDB starts replication for a registered database.
// The context is checked for cancellation before opening.
// Note: db.Open() itself does not support cancellation.
func (s *Store) EnableDB(ctx context.Context, path string) error {
db := s.FindDB(path)
if db == nil {
return fmt.Errorf("database not found: %s", path)
}
if db.IsOpen() {
return fmt.Errorf("database already enabled: %s", path)
}
// Check for cancellation before starting open
if err := ctx.Err(); err != nil {
return fmt.Errorf("enable database: %w", err)
}
if err := db.Open(); err != nil {
return fmt.Errorf("open database: %w", err)
}
return nil
}
// DisableDB stops replication for a database.
func (s *Store) DisableDB(ctx context.Context, path string) error {
db := s.FindDB(path)
if db == nil {View on GitHub (pinned to 4ed7a308f6)
Solutions
- Check db.IsOpen() (or via IPC status) before calling EnableDB
- Treat this error as idempotent success if your goal is 'ensure enabled' — use errors.Is/string match on the message
- Serialize enable/disable calls through a single control path or mutex
- Use DisableDB before re-enabling if you need a restart
Example fix
// before
if err := store.EnableDB(ctx, path); err != nil { return err } // fails if already enabled
// after
if db := store.FindDB(path); db != nil && !db.IsOpen() {
if err := store.EnableDB(ctx, path); err != nil { return err }
} Defensive patterns
Strategy: validation
Validate before calling
if db := store.FindDB(path); db != nil && db.IsOpen() {
return nil // already enabled, nothing to do
} Try / catch
if err := store.EnableDB(ctx, path); err != nil {
if strings.Contains(err.Error(), "already enabled") {
return nil // treat as idempotent success
}
return err
} Prevention
- Make enable/disable idempotent at the call site
- Serialize control operations through one goroutine or mutex
- Track desired vs actual DB state before issuing IPC commands
When it happens
Trigger: Calling Store.EnableDB(ctx, path) (IPC handleStart) on a DB that is already open — e.g. it was enabled earlier via IPC, or opened at startup by the config.
Common situations: Duplicate IPC 'start' commands (client retry, double-click in an operator UI); racing goroutines both calling EnableDB; assuming a DB was disabled when it was only paused.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- database already disabled: %s
- open database: %w
- open database: %w
- set journal mode: %w
- create table %s: %w
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/1581358a118e3ce6.
Report an issue: GitHub.