benbjohnson/litestream · error

enable database: %w

Error message

enable database: %w

What it means

EnableDB checks the context for cancellation before performing the (uncancellable) db.Open(); if ctx is already done it returns 'enable database: <ctx err>'. This prevents starting a long, non-cancellable open for a caller that has already given up.

Source

Thrown at store.go:393

	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 {
		return fmt.Errorf("database not found: %s", path)
	}

	if !db.IsOpen() {
		return fmt.Errorf("database already disabled: %s", path)

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Retry EnableDB with a fresh context (context.WithTimeout) if the enable is still wanted
  2. Check ctx.Err() at the call site before invoking EnableDB to fail fast
  3. Avoid reusing a per-request context for background operations; derive a background context for long-lived control ops
  4. Increase the handler deadline if opens legitimately take long

Example fix

// before
err := store.EnableDB(ctx, path) // ctx already cancelled
// after
enableCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
err := store.EnableDB(enableCtx, path)
Defensive patterns

Strategy: retry

Validate before calling

if err := ctx.Err(); err != nil {
    ctx = context.Background() // derive fresh context for control ops
}

Try / catch

if err := store.EnableDB(ctx, path); err != nil {
    if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
        enableCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
        defer cancel()
        return store.EnableDB(enableCtx, path)
    }
    return err
}

Prevention

When it happens

Trigger: Store.EnableDB(ctx, path) called with a cancelled or timed-out context — e.g. an IPC request whose deadline expired before reaching this point, or shutdown cancellation racing an enable.

Common situations: HTTP/IPC handlers with request deadlines that elapsed while waiting on the store lock; graceful shutdown cancelling in-flight control commands; a caller reusing an exhausted request context.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


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