benbjohnson/litestream · warning

database already enabled

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.

Solutions

  1. Check db.IsOpen() (or via IPC status) before calling EnableDB
  2. Treat this error as idempotent success if your goal is 'ensure enabled' — use errors.Is/string match on the message
  3. Serialize enable/disable calls through a single control path or mutex
  4. 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

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


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

Appendix: 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)