Kareadita/Kavita · warning · KavitaException
cant-delete-default-profile
Error message
cant-delete-default-profile
What it means
Thrown by DeleteReadingProfile when the resolved profile has Kind == ReadingProfileKind.Default. The Default profile is system-managed and cannot be removed; only User (and Implicit-promoted) profiles are deletable. The 'cant-delete-default-profile' localization key is surfaced. This is a business-rule guard, not a data fault.
Source
Thrown at Kavita.Services/Reading/ReadingProfileService.cs:181
newProfile.Name = $"Implicit Profile for {seriesId}";
newProfile.NormalizedName = newProfile.Name.ToNormalized();
if (activeDeviceId != null)
{
newProfile.DeviceIds.Add(activeDeviceId.Value);
}
user.ReadingProfiles.Add(newProfile);
await unitOfWork.CommitAsync();
return mapper.Map<UserReadingProfileDto>(newProfile);
}
public async Task DeleteReadingProfile(int userId, int profileId)
{
var profile = await unitOfWork.AppUserReadingProfileRepository.GetUserProfile(userId, profileId);
if (profile == null) throw new KavitaException("profile-doesnt-exist");
if (profile.Kind == ReadingProfileKind.Default) throw new KavitaException("cant-delete-default-profile");
unitOfWork.AppUserReadingProfileRepository.Remove(profile);
await unitOfWork.CommitAsync();
}
public async Task SetSeriesProfiles(int userId, List<int> profileIds, int seriesId)
{
var profiles = await unitOfWork.AppUserReadingProfileRepository.GetProfilesForUser(userId);
var selectedProfiles = profiles
.Where(rp => profileIds.Contains(rp.Id))
.ToList();
if (selectedProfiles.Count != profileIds.Count) throw new KavitaException("profile-doesnt-exist");
DeviceOverlapGuard(selectedProfiles);
var allDeviceIds = selectedProfiles.SelectMany(p => p.DeviceIds).Distinct().ToList();
View on GitHub (pinned to 9c3e540000)
Solutions
- Do not attempt to delete the Default profile; it is permanent.
- Disable/hide the delete control when profile.Kind == Default in the UI.
- If custom settings are wanted, edit the Default profile or create a new User profile instead.
Defensive patterns
Strategy: validation
Validate before calling
var profile = await unitOfWork.AppUserReadingProfileRepository.GetUserProfile(userId, profileId);
if (profile?.Kind == ReadingProfileKind.Default) return BadRequest("cant-delete-default-profile");
await profileService.DeleteReadingProfile(userId, profileId); Type guard
public static bool IsDeletable(AppUserReadingProfile p) => p.Kind != ReadingProfileKind.Default;
Try / catch
try { await profileService.DeleteReadingProfile(userId, profileId); }
catch (KavitaException ex) when (ex.Message == "cant-delete-default-profile") { return BadRequest(ex.Message); } Prevention
- Hide/disable the delete control for the Default profile in the UI.
- Never target the Default profile id in scripted deletes.
- Edit the Default profile or create a User profile instead of deleting.
When it happens
Trigger: The UI or API client issues a delete against the user's Default reading profile.
Common situations: Misconfigured UI offering delete on the default profile; scripted/admin call targeting the default id; user misunderstanding that the default is permanent.
Related errors
- name-already-in-use
- cant-assign-devices-to-default
- reading-profiles-device-overlap
- profile-does-not-exist
- reading-list-name-exists
AI-assisted analysis of Kareadita/Kavita@9c3e540000 (2026-08-13).
Data as JSON: /api/errors/242087d7fd840c4c.
Report an issue: GitHub.