Kareadita/Kavita · error · KavitaException

volume-doesnt-exist

Error message

volume-doesnt-exist

What it means

Thrown in the same rebuild branch of KoreaderService.SaveProgress: the chapter was found and has a VolumeId, but GetVolumeByIdAsync returns null. The chapter references a volume that doesn't exist — a deeper orphan in the chapter → volume chain. Only reachable when user progress is missing for the chapter.

Source

Thrown at Kavita.Services/KoreaderService.cs:48

    public async Task SaveProgress(KoreaderBookDto koreaderBookDto, int userId, CancellationToken ct = default)
    {
        logger.LogDebug("Saving KOReader progress for User ({UserId}): {KoreaderProgress} - {KoreaderHash}",
            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);

View on GitHub (pinned to 9c3e540000)

Solutions

  1. Force-scan the series to rebuild the volume → chapter → file graph.
  2. Inspect the DB for chapters whose VolumeId has no matching volume row and repair or remove them.
  3. If the volume was intentionally deleted, the chapter should have been re-homed; re-add the source files and scan.
  4. Catch KavitaException upstream and advise the user to rescan rather than silently dropping progress.
Defensive patterns

Strategy: try-catch

Validate before calling

var volume = await unitOfWork.VolumeRepository.GetVolumeByIdAsync(chapter.VolumeId, ct: ct);
if (volume == null)
    return Conflict(new { error = "volume-doesnt-exist", hint = "Rescan the series." });

Try / catch

try { await koreaderService.SaveProgress(dto, userId, ct); }
catch (KavitaException ex) when (ex.Message.Contains("volume-doesnt-exist"))
{ /* request a rescan; degrade gracefully rather than retry in a loop */ }

Prevention

When it happens

Trigger: KOReader progress sync where Chapter.VolumeId points at a deleted volume row; happens after a volume restructure (e.g. special/normal volume recategorization) that orphaned chapters.

Common situations: Library scan reorganized volumes and left chapters pointing at removed volume IDs; partial DB restore or interrupted migration.

Related errors


AI-assisted analysis of Kareadita/Kavita@9c3e540000 (2026-08-13). Data as JSON: /api/errors/8c73d0ade27b4af7. Report an issue: GitHub.