{"record":{"id":"9b435b60550f799d","repo":"Kareadita/Kavita","slug":"total-backups","errorCode":"total-backups","errorMessage":"total-backups","messagePattern":"total-backups","errorType":"exception","errorClass":"KavitaException","httpStatus":400,"severity":"warning","filePath":"Kavita.Services/SettingsService.cs","lineNumber":445,"sourceCode":"                setting.Value = directoryService.FileSystem.Path.GetFullPath(bookmarkDirectory);\n                unitOfWork.SettingsRepository.Update(setting);\n                updateBookmarks = true;\n\n            }\n\n            if (setting.Key == ServerSettingKey.AllowStatCollection &&\n                updateSettingsDto.AllowStatCollection + string.Empty != setting.Value)\n            {\n                setting.Value = updateSettingsDto.AllowStatCollection + string.Empty;\n                unitOfWork.SettingsRepository.Update(setting);\n            }\n\n            if (setting.Key == ServerSettingKey.TotalBackups &&\n                updateSettingsDto.TotalBackups + string.Empty != setting.Value)\n            {\n                if (updateSettingsDto.TotalBackups > 30 || updateSettingsDto.TotalBackups < 1)\n                {\n                    throw new KavitaException(\"total-backups\");\n                }\n\n                setting.Value = updateSettingsDto.TotalBackups + string.Empty;\n                unitOfWork.SettingsRepository.Update(setting);\n            }\n\n            if (setting.Key == ServerSettingKey.TotalLogs &&\n                updateSettingsDto.TotalLogs + string.Empty != setting.Value)\n            {\n                if (updateSettingsDto.TotalLogs > 30 || updateSettingsDto.TotalLogs < 1)\n                {\n                    throw new KavitaException(\"total-logs\");\n                }\n\n                setting.Value = updateSettingsDto.TotalLogs + string.Empty;\n                unitOfWork.SettingsRepository.Update(setting);\n            }\n","sourceCodeStart":427,"sourceCodeEnd":463,"githubUrl":"https://github.com/Kareadita/Kavita/blob/9c3e5400007f8a0282f7d883f2ad5e71716e514d/Kavita.Services/SettingsService.cs#L427-L463","documentation":"Range-validation error for the 'Total Backups' server setting. Kavita retains at most 30 backup snapshots (CleanupService reads this value as the day/file threshold), so the SettingsService rejects any retention count outside [1, 30]. It is thrown as a KavitaException with the localization key 'total-backups' while iterating server settings, only when the submitted value actually differs from the stored one. SettingsController catches it and returns HTTP 400 with the translated message 'Total Backups must be between 1 and 30'.","triggerScenarios":"POST /api/settings with totalBackups set to 0, a negative number, or a value greater than 30, where the value differs from the currently persisted setting (the guard is skipped if the value is unchanged).","commonSituations":"Admin enters 0 intending 'no backups'; admin enters a large number (e.g. 100) expecting unlimited retention; restoring/importing a config blob from another instance whose value was outside the allowed range; a UI form default of 0 slipping through unvalidated.","solutions":["Set totalBackups to an integer between 1 and 30 inclusive and resend the request.","Add client-side range validation (1..30) on the backups field and disable submit while invalid.","Re-GET /api/settings to read the current value and reconcile the form before saving.","If automating config import, clamp the imported value into [1, 30] before posting."],"exampleFix":"// before\n{ totalBackups: 0 } // 'keep no backups'\n\n// after\n{ totalBackups: 7 } // valid: 1..30","handlingStrategy":"validation","validationCode":"// Run before POST /api/settings\nfunction isValidRetention(n: unknown): n is number {\n  return typeof n === 'number' && Number.isInteger(n) && n >= 1 && n <= 30;\n}\nif (!isValidRetention(dto.totalBackups)) {\n  showError('Total Backups must be between 1 and 30');\n  return; // do not call the API\n}","typeGuard":"function isValidRetention(n: unknown): n is number {\n  return typeof n === 'number' && Number.isInteger(n) && n >= 1 && n <= 30;\n}","tryCatchPattern":null,"preventionTips":["Bind the backups input to a number stepper clamped to [1, 30] and disable save while invalid.","Mirror the exact server bounds (1..30) in client validation so messages match.","On any 400 with 'total-backups', re-GET settings and re-sync the form."],"tags":["settings","validation","backups","range","admin"],"backgroundTag":null,"analyzedSha":"9c3e5400007f8a0282f7d883f2ad5e71716e514d","analyzedAt":"2026-08-13T19:06:05.897Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}