benbjohnson/litestream · error

close db: %w

Error message

close db: %w

What it means

UnregisterDB removes a database from the Store's registry and then closes it. This error wraps any failure returned by db.Close(ctx) during that teardown, so the DB may be unregistered but not fully shut down (WAL monitor, replicas, executors). Because it's a wrapped error, the underlying cause (e.g. replica close failure, context cancellation) is in the %w chain.

Source

Thrown at store.go:372

	var db *DB
	for i, existing := range s.dbs {
		if existing.Path() == path {
			idx = i
			db = existing
			break
		}
	}

	if db == nil {
		s.mu.Unlock()
		return nil
	}

	s.dbs = slices.Delete(s.dbs, idx, idx+1)
	s.mu.Unlock()

	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)
	}

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Inspect the wrapped cause with errors.Unwrap / %v to see why Close failed
  2. Retry UnregisterDB once the in-flight sync completes; Close is retriable after the transient condition clears
  3. Pass a context with an adequate timeout instead of one already near cancellation
  4. Check replica/storage connectivity if the wrapped error points at replica cleanup

Example fix

// before
if err := db.Close(ctx); err != nil {
    return fmt.Errorf("close db: %w", err)
}
// after
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := db.Close(ctx); err != nil {
    return fmt.Errorf("close db: %w", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

if db := store.FindDB(path); db == nil {
    return fmt.Errorf("db not registered: %s", path)
}

Try / catch

if err := store.UnregisterDB(ctx, path); err != nil {
    var ctxErr error
    if errors.As(err, &ctxErr) && errors.Is(ctxErr, context.Canceled) {
        // retry with fresh context
    }
    return fmt.Errorf("unregister %s: %w", path, err)
}

Prevention

When it happens

Trigger: Calling Store.UnregisterDB(ctx, path) for a registered DB whose Close fails — e.g. db.Open() active, replica sync in flight, or ctx cancelled while close waits for locks.

Common situations: IPC-driven unregister (handleUnregister) or config reload (removeDatabase/removeDatabasesUnder) while the database is mid-sync; shutting down litestream with an open DB; cancelling the context passed to UnregisterDB during a slow close.

Understand the failure class

Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.

Related errors


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