benbjohnson/litestream · error
close database: %w
Error message
close database: %w
What it means
Wraps db.Close failure in Store.DisableDB. Fires when disabling a database: the DB was open and eligible for disable, but closing its resources (WAL monitoring, open handles) failed, so the disable operation is reported as failed instead of silently leaving it half-closed.
Source
Thrown at store.go:415
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)
}
if err := db.Close(ctx); err != nil {
return fmt.Errorf("close database: %w", err)
}
return nil
}
// SyncDBResult holds the result of a sync operation.
type SyncDBResult struct {
TXID uint64
ReplicatedTXID uint64
Changed bool
}
// SyncDB forces an immediate sync for a database. If wait is true, blocks
// until both WAL-to-LTX and LTX-to-remote sync complete. If wait is false,
// only performs the WAL-to-LTX sync and lets the replica monitor handle upload.
// Lock waits are context-aware: the timeout is honored while waiting for
// the database sync executor and the replica sync lock.
func (s *Store) SyncDB(ctx context.Context, path string, wait bool) (SyncDBResult, error) {View on GitHub (pinned to 4ed7a308f6)
Solutions
- Inspect the wrapped cause to identify whether it was cancellation or a replica/executor error
- Retry DisableDB with a fresh, longer-timeout context after in-flight syncs drain
- Check replica/storage connectivity if the cause points at replica cleanup
- Ensure no other goroutine is calling SyncDB concurrently with the disable
Example fix
// before
ctx, cancel := context.WithTimeout(ctx, 1*time.Millisecond) // too short
if err := store.DisableDB(ctx, path); err != nil { ... }
// after
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := store.DisableDB(ctx, path); err != nil { ... } Defensive patterns
Strategy: retry
Try / catch
if err := store.DisableDB(ctx, path); err != nil {
var cause error
errors.As(err, &cause)
if errors.Is(cause, context.DeadlineExceeded) || errors.Is(cause, context.Canceled) {
ctx2, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
return store.DisableDB(ctx2, path)
}
return err
} Prevention
- Give DisableDB a generous timeout — it waits for syncs to drain
- Avoid issuing SyncDB concurrently with DisableDB
- Log the full error chain to distinguish cancellation from replica failures
When it happens
Trigger: Store.DisableDB(ctx, path) where db.Close fails — sync executor or replica sync holding the lock, ctx cancelled mid-close, or an internal close error on the SQLite handle.
Common situations: Disabling a database during active replication traffic; a context deadline expiring while Close waits for the sync executor; shutdown racing a disable command.
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/4f0250f51f2cd37d.
Report an issue: GitHub.