{"record":{"id":"c443700dd742bffd","repo":"AdguardTeam/AdGuardHome","slug":"deleting-sessions-w","errorCode":null,"errorMessage":"deleting sessions: %w","messagePattern":"deleting sessions: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/aghuser/sessionstorage.go","lineNumber":215,"sourceCode":"\tctx context.Context,\n\tbkt *bbolt.Bucket,\n) (removed int, err error) {\n\tinvalidSessions := [][]byte{}\n\n\terr = bkt.ForEach(ds.bboltSessionHandler(ctx, &invalidSessions))\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"iterating over sessions: %w\", err)\n\t}\n\n\tvar errs []error\n\tfor _, s := range invalidSessions {\n\t\tif err = bkt.Delete(s); err != nil {\n\t\t\terrs = append(errs, err)\n\t\t}\n\t}\n\n\tif err = errors.Join(errs...); err != nil {\n\t\treturn 0, fmt.Errorf(\"deleting sessions: %w\", err)\n\t}\n\n\treturn len(invalidSessions), nil\n}\n\n// bboltSessionHandler returns a function for [bbolt.Bucket.ForEach] that\n// iterates over stored sessions, deserializes them, and logs any errors\n// encountered.  The returned error is always nil, as these errors are\n// considered non-critical to stop the iteration process.\nfunc (ds *DefaultSessionStorage) bboltSessionHandler(\n\tctx context.Context,\n\tinvalidSessions *[][]byte,\n) (fn func(k, v []byte) (err error)) {\n\tnow := ds.clock.Now()\n\n\treturn func(k, v []byte) (err error) {\n\t\ts, err := bboltDecode(v)\n\t\tif err != nil {","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/AdguardTeam/AdGuardHome/blob/b41aefbe51c8dde65e2c50f093996afa0502edf9/internal/aghuser/sessionstorage.go#L197-L233","documentation":"Thrown when DefaultSessionStorage fails to delete one or more expired/invalid sessions from the underlying bbolt bucket during processSessions (invoked from loadSessions). Individual bkt.Delete errors are joined with errors.Join and wrapped as 'deleting sessions'. It indicates the bbolt database rejected a Delete inside the read/write transaction used at startup to purge stale sessions.","triggerScenarios":"Calling NewSessionStorage / loadSessions on a database where deleting a key fails: bucket was deleted concurrently, transaction was closed, database file is corrupted, or the bucket is read-only / file permissions changed.","commonSituations":"Corrupted session.db bbolt file after a crash; two AdGuard Home instances sharing the same data/work folder; read-only filesystem or permission loss on the data directory; upgrading between versions with an incompatible bbolt schema.","solutions":["Stop other AdGuard Home instances using the same data directory and retry","Check file permissions / read-only mounts on the sessions db file","Run bbolt consistency checks (e.g. bbolt check) on the database file","If corruption is confirmed, delete the sessions db file — sessions are ephemeral and will be recreated"],"exampleFix":"// before: sharing one work dir between two instances\n// after: give each instance its own -w work directory\n./AdGuardHome -w /var/lib/adguardhome1\n./AdGuardHome -w /var/lib/adguardhome2","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// ignore deletion failures during load; sessions are ephemeral and purged next cycle\n_, err := storage.LoadSessions(ctx)\nif err != nil && !strings.Contains(err.Error(), \"deleting sessions\") { return err }","preventionTips":["Keep one process per work directory","Monitor disk health of the data volume","Back up but never hand-edit sessions.db"],"tags":["bbolt","storage","sessions","startup"],"backgroundTag":"database-corruption","analyzedSha":"b41aefbe51c8dde65e2c50f093996afa0502edf9","analyzedAt":"2026-08-27T04:57:55.097Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}