Kareadita/Kavita · error · KavitaException
series-doesnt-exist
Error message
series-doesnt-exist
What it means
Thrown by PromoteImplicitProfile when the series bound to the implicit profile (profileToPromote.SeriesIds[0]) no longer exists in the DB. The code comment 'Shouldn't happen' marks it as an invariant-violation guard: an implicit profile should always reference a live series. The 'series-doesnt-exist' localization key is surfaced.
Source
Thrown at Kavita.Services/Reading/ReadingProfileService.cs:110
if (profileToPromote.Kind != ReadingProfileKind.Implicit) throw new KavitaException("profile-not-implicit");
var seriesId = profileToPromote.SeriesIds[0]; // An Implicit series can only be bound to 1 Series
// Check if there are any reading profiles (Series) already bound to the series (and device)
var existingSeriesProfile = allUserProfiles
.Where(rp => rp.Kind == ReadingProfileKind.User)
.Where(rp => activeDeviceId == null || rp.DeviceIds.Contains(activeDeviceId.Value))
.FirstOrDefault(rp => rp.SeriesIds.Contains(seriesId));
if (existingSeriesProfile != null)
{
existingSeriesProfile.SeriesIds.Remove(seriesId);
unitOfWork.AppUserReadingProfileRepository.Update(existingSeriesProfile);
}
// Convert the implicit profile into a proper Series
var series = await unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId);
if (series == null) throw new KavitaException("series-doesnt-exist"); // Shouldn't happen
profileToPromote.Kind = ReadingProfileKind.User;
profileToPromote.Name = await localizationService.TranslateAsync(userId, "generated-reading-profile-name", series.Name);
profileToPromote.Name = EnsureUniqueProfileName(allUserProfiles, profileToPromote.Name);
profileToPromote.NormalizedName = profileToPromote.Name.ToNormalized();
unitOfWork.AppUserReadingProfileRepository.Update(profileToPromote);
await unitOfWork.CommitAsync();
return mapper.Map<UserReadingProfileDto>(profileToPromote);
}
private static string EnsureUniqueProfileName(IList<AppUserReadingProfile> allUserProfiles, string name)
{
var counter = 1;
var newName = name;
while (allUserProfiles.Any(p => p.Name == newName))
{View on GitHub (pinned to 9c3e540000)
Solutions
- Delete or reset the orphaned implicit profile so it stops referencing the missing series.
- Trigger a library scan to confirm series state, then recreate the implicit profile from the reader.
- Investigate data integrity if this fires — the comment implies it should be unreachable.
Defensive patterns
Strategy: validation
Validate before calling
var series = await unitOfWork.SeriesRepository.GetSeriesByIdAsync(profileToPromote.SeriesIds[0]);
if (series == null) { // clean up orphaned implicit profile instead of throwing
unitOfWork.AppUserReadingProfileRepository.Remove(profileToPromote);
await unitOfWork.CommitAsync();
return;
} Try / catch
try { await profileService.PromoteImplicitProfile(userId, profileId, activeDeviceId); }
catch (KavitaException ex) when (ex.Message == "series-doesnt-exist") { _logger.LogError("Orphaned implicit profile {Id}", profileId); return NotFound(); } Prevention
- Cascade-delete or reset implicit profiles when their series is removed.
- Treat this throw as a data-integrity signal — investigate if it fires.
- Validate series existence before dereferencing SeriesIds[0].
When it happens
Trigger: The series tied to the implicit profile was deleted between profile creation and promotion, leaving a dangling reference in SeriesIds.
Common situations: Series removed by an admin/library rescan while an implicit profile lingered; data-integrity drift after migration; manual DB edits broke the reference.
Related errors
- series-doesnt-exist
- series-doesnt-exist
- series-restricted-age-restriction
- chapter-doesnt-exist
- volume-doesnt-exist
AI-assisted analysis of Kareadita/Kavita@9c3e540000 (2026-08-13).
Data as JSON: /api/errors/c19e19396b3a1e54.
Report an issue: GitHub.