{"record":{"id":"384ef51656820d96","repo":"benbjohnson/litestream","slug":"enable-database-w","errorCode":null,"errorMessage":"enable database: %w","messagePattern":"enable database: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"store.go","lineNumber":393,"sourceCode":"\treturn nil\n}\n\n// EnableDB starts replication for a registered database.\n// The context is checked for cancellation before opening.\n// Note: db.Open() itself does not support cancellation.\nfunc (s *Store) EnableDB(ctx context.Context, path string) error {\n\tdb := s.FindDB(path)\n\tif db == nil {\n\t\treturn fmt.Errorf(\"database not found: %s\", path)\n\t}\n\n\tif db.IsOpen() {\n\t\treturn fmt.Errorf(\"database already enabled: %s\", path)\n\t}\n\n\t// Check for cancellation before starting open\n\tif err := ctx.Err(); err != nil {\n\t\treturn fmt.Errorf(\"enable database: %w\", err)\n\t}\n\n\tif err := db.Open(); err != nil {\n\t\treturn fmt.Errorf(\"open database: %w\", err)\n\t}\n\n\treturn nil\n}\n\n// DisableDB stops replication for a database.\nfunc (s *Store) DisableDB(ctx context.Context, path string) error {\n\tdb := s.FindDB(path)\n\tif db == nil {\n\t\treturn fmt.Errorf(\"database not found: %s\", path)\n\t}\n\n\tif !db.IsOpen() {\n\t\treturn fmt.Errorf(\"database already disabled: %s\", path)","sourceCodeStart":375,"sourceCodeEnd":411,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/store.go#L375-L411","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Retry EnableDB with a fresh context (context.WithTimeout) if the enable is still wanted","Check ctx.Err() at the call site before invoking EnableDB to fail fast","Avoid reusing a per-request context for background operations; derive a background context for long-lived control ops","Increase the handler deadline if opens legitimately take long"],"exampleFix":"// before\nerr := store.EnableDB(ctx, path) // ctx already cancelled\n// after\nenableCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\ndefer cancel()\nerr := store.EnableDB(enableCtx, path)","handlingStrategy":"retry","validationCode":"if err := ctx.Err(); err != nil {\n    ctx = context.Background() // derive fresh context for control ops\n}","typeGuard":null,"tryCatchPattern":"if err := store.EnableDB(ctx, path); err != nil {\n    if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {\n        enableCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\n        defer cancel()\n        return store.EnableDB(enableCtx, path)\n    }\n    return err\n}","preventionTips":["Derive long-lived control contexts from context.Background, not request contexts","Set generous deadlines for operations that trigger db.Open","Check ctx.Err() before issuing store operations"],"tags":["context","cancellation","database"],"backgroundTag":"request-timeout","analyzedSha":"4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3","analyzedAt":"2026-09-06T18:29:25.564Z","contentChangedAt":"2026-09-06T18:29:25.564Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}