Kareadita/Kavita · error · KavitaException
series-doesnt-exist
Error message
series-doesnt-exist
What it means
Thrown in the same rebuild branch: the volume exists and carries a SeriesId, but GetSeriesDtoByIdAsync returns null. The volume points at a missing series — the top of the orphan chain. Reaching here means chapter and volume resolved but series did not, which is rare and indicates a broken volume → series link.
Source
Thrown at Kavita.Services/KoreaderService.cs:51
userId, koreaderBookDto.progress.Sanitize(), koreaderBookDto.document.Sanitize());
var file = await unitOfWork.MangaFileRepository.GetByKoreaderHash(koreaderBookDto.document, ct);
if (file == null)
{
logger.LogWarning("KOReader progress for unknown book: {BookHash}. Run a force scan on the series to generate KOReader hashes", koreaderBookDto.document.Sanitize());
throw new KavitaException(await localizationService.TranslateAsync(userId, "file-missing"));
}
var userProgressDto = await unitOfWork.AppUserProgressRepository.GetUserProgressDtoAsync(file.ChapterId, userId, ct);
if (userProgressDto == null)
{
var chapterDto = await unitOfWork.ChapterRepository.GetChapterDtoAsync(file.ChapterId, userId, ct);
if (chapterDto == null) throw new KavitaException(await localizationService.TranslateAsync(userId, "chapter-doesnt-exist"));
var volumeDto = await unitOfWork.VolumeRepository.GetVolumeByIdAsync(chapterDto.VolumeId, ct: ct);
if (volumeDto == null) throw new KavitaException(await localizationService.TranslateAsync(userId, "volume-doesnt-exist"));
var seriesDto = await unitOfWork.SeriesRepository.GetSeriesDtoByIdAsync(volumeDto.SeriesId, userId, ct);
if (seriesDto == null) throw new KavitaException(await localizationService.TranslateAsync(userId, "series-doesnt-exist"));
userProgressDto = new ProgressDto()
{
PageNum = 0, // This is updated in KoreaderHelper.UpdateProgressDto
ChapterId = file.ChapterId,
VolumeId = chapterDto.VolumeId,
SeriesId = seriesDto.Id,
LibraryId = seriesDto.LibraryId
};
}
// Update the bookScrollId if possible
var reportedProgress = koreaderBookDto.progress;
KoreaderHelper.UpdateProgressDto(userProgressDto, koreaderBookDto.progress);
logger.LogDebug("Converted KOReader progress from {ProgressEncoding} to Page {PageNum} with ScrollId: {ScrollId}. For Chapter {ChapterId} in Series {SeriesId}",
reportedProgress.Sanitize(), userProgressDto.PageNum, userProgressDto.BookScrollId?.Sanitize() ?? string.Empty,
userProgressDto.ChapterId, userProgressDto.SeriesId);View on GitHub (pinned to 9c3e540000)
Solutions
- Force-scan the library to rebuild the full series → volume → chapter → file hierarchy.
- Check the DB for volumes whose SeriesId has no matching series row; clean up or reparent.
- If the series was deleted intentionally, ensure its volumes/chapters/manga_files were also removed.
- Surface a clear 'rescan required' message to the end user rather than retrying the sync.
Defensive patterns
Strategy: try-catch
Validate before calling
var series = await unitOfWork.SeriesRepository.GetSeriesDtoByIdAsync(volume.SeriesId, userId, ct);
if (series == null)
return Conflict(new { error = "series-doesnt-exist", hint = "Rescan the library." }); Try / catch
try { await koreaderService.SaveProgress(dto, userId, ct); }
catch (KavitaException ex) when (ex.Message.Contains("series-doesnt-exist"))
{ /* full library rescan; do not retry until hierarchy rebuilt */ } Prevention
- Cascade-delete volumes/chapters/files when a series is removed.
- Run library scans after bulk imports/deletes.
- Monitor for volumes whose SeriesId is orphaned.
When it happens
Trigger: KOReader SaveProgress with a volume whose SeriesId is absent from the series table (deleted series with orphaned volumes, partial restore).
Common situations: Series deleted but volumes/chapters left behind; interrupted scan/refresh left the hierarchy half-built; manual DB surgery removed a series without cascading.
Related errors
AI-assisted analysis of Kareadita/Kavita@9c3e540000 (2026-08-13).
Data as JSON: /api/errors/100f8b55b8bf1159.
Report an issue: GitHub.