{"record":{"id":"e6096619395719c7","repo":"AdguardTeam/AdGuardHome","slug":"closing-database-w","errorCode":null,"errorMessage":"closing database: %w","messagePattern":"closing database: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/stats/stats.go","lineNumber":543,"sourceCode":"func (s *StatsCtx) clear() (err error) {\n\tdefer func() { err = errors.Annotate(err, \"clearing: %w\") }()\n\n\tdb := s.db.Swap(nil)\n\tif db != nil {\n\t\tvar tx *bbolt.Tx\n\t\ttx, err = db.Begin(true)\n\t\tif err != nil {\n\t\t\ts.logger.Error(\"opening transaction\", slogutil.KeyError, err)\n\t\t} else if err = finishTxn(tx, false); err != nil {\n\t\t\t// Don't wrap the error since it's informative enough as is.\n\t\t\treturn err\n\t\t}\n\n\t\t// Active transactions will continue using database, but new ones won't\n\t\t// be created.\n\t\terr = db.Close()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"closing database: %w\", err)\n\t\t}\n\n\t\t// All active transactions are now closed.\n\t\ts.logger.Debug(\"database closed\")\n\t}\n\n\terr = os.Remove(s.filename)\n\tif err != nil {\n\t\ts.logger.Error(\"removing\", slogutil.KeyError, err)\n\t}\n\n\terr = s.openDB()\n\tif err != nil {\n\t\ts.logger.Error(\"opening database\", slogutil.KeyError, err)\n\t}\n\n\t// Use defer to unlock the mutex as soon as possible.\n\tdefer s.logger.Debug(\"cleared\")","sourceCodeStart":525,"sourceCodeEnd":561,"githubUrl":"https://github.com/AdguardTeam/AdGuardHome/blob/b41aefbe51c8dde65e2c50f093996afa0502edf9/internal/stats/stats.go#L525-L561","documentation":"During stats reset (clear, triggered by handleStatsReset or setLimit), the code closes the old bbolt database before recreating it; db.Close() failed and is wrapped as 'closing database'.","triggerScenarios":"Resetting stats (POST to the stats reset endpoint) or changing the limit while the database has open transactions or is otherwise unable to close cleanly.","commonSituations":"Concurrent reads/writes still holding transactions when reset happens; file descriptor or lock issues on the stats DB path.","solutions":["Retry the reset once the system is idle / no concurrent stats queries are running","Check for leaked transactions or long-running readers on the DB","If the DB is corrupt, stop the service and remove the stats file"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := s.Clear(); err != nil { log.Printf(\"reset failed: %v; retrying\", err); time.Sleep(time.Second); err = s.Clear() }","preventionTips":["Quiesce stats queries before reset","Avoid changing limit under load"],"tags":["stats","database","bbolt","reset"],"backgroundTag":"database-close-failed","analyzedSha":"b41aefbe51c8dde65e2c50f093996afa0502edf9","analyzedAt":"2026-08-27T04:57:55.097Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}