{"record":{"id":"f14819dd03dd8c7f","repo":"benbjohnson/litestream","slug":"database-already-disabled-s","errorCode":null,"errorMessage":"database already disabled: %s","messagePattern":"database already disabled: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"store.go","lineNumber":411,"sourceCode":"\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)\n\t}\n\n\tif err := db.Close(ctx); err != nil {\n\t\treturn fmt.Errorf(\"close database: %w\", err)\n\t}\n\n\treturn nil\n}\n\n// SyncDBResult holds the result of a sync operation.\ntype SyncDBResult struct {\n\tTXID           uint64\n\tReplicatedTXID uint64\n\tChanged        bool\n}\n\n// SyncDB forces an immediate sync for a database. If wait is true, blocks\n// until both WAL-to-LTX and LTX-to-remote sync complete. If wait is false,","sourceCodeStart":393,"sourceCodeEnd":429,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/store.go#L393-L429","documentation":"DisableDB refuses to close a database that is not currently open (db.IsOpen() false). Closing an already-disabled DB would be a no-op at best and a state bug at worst, so the Store reports it as an invalid state transition.","triggerScenarios":"Store.DisableDB(ctx, path) on a DB that was registered but never opened, or already disabled by a previous DisableDB call.","commonSituations":"Duplicate 'stop' IPC commands from retries; stopping a DB that failed to open earlier; racing two operators disabling the same database.","solutions":["Check db.IsOpen() before calling DisableDB and skip if already closed","Treat this as idempotent success when the desired end state is 'disabled'","Serialize enable/disable operations through one control loop","Re-check state after concurrent operations rather than assuming the DB is open"],"exampleFix":"// before\nif err := store.DisableDB(ctx, path); err != nil { return err } // errors if already disabled\n// after\nif db := store.FindDB(path); db != nil && db.IsOpen() {\n    return store.DisableDB(ctx, path)\n}\nreturn nil","handlingStrategy":"validation","validationCode":"if db := store.FindDB(path); db == nil || !db.IsOpen() {\n    return nil // already disabled or absent\n}","typeGuard":null,"tryCatchPattern":"if err := store.DisableDB(ctx, path); err != nil {\n    if strings.Contains(err.Error(), \"already disabled\") {\n        return nil // idempotent success\n    }\n    return err\n}","preventionTips":["Check IsOpen before disable; skip if closed","Centralize enable/disable state management to prevent races","Design stop handlers to be idempotent"],"tags":["database","state","duplicate-operation"],"backgroundTag":"invalid-state-transition","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"}