Kareadita/Kavita · error · OpdsException
chapter-doesnt-exist
Error message
chapter-doesnt-exist
What it means
Thrown by OpdsService.GetItemsFromChapter when the chapter is not found within the volume's chapter collection. After the series and volume checks pass, the service does volume.Chapters.FirstOrDefault(c => c.Id == chapterId). If this returns null, the chapterId does not belong to the validated volume. This is distinct from a volume-level null: the volume exists but doesn't contain this chapter.
Source
Thrown at Kavita.Services/OpdsService.cs:704
var chapterId = request.ChapterId;
var series = await unitOfWork.SeriesRepository.GetSeriesDtoByIdAsync(seriesId, userId, ct);
if (series == null)
{
throw new OpdsException(await localizationService.TranslateAsync(userId, "series-doesnt-exist"));
}
var volume = await unitOfWork.VolumeRepository.GetVolumeDtoAsync(volumeId, userId, ct);
if (volume == null)
{
throw new OpdsException(await localizationService.TranslateAsync(userId, "volume-doesnt-exist"));
}
var libraryType = await unitOfWork.LibraryRepository.GetLibraryTypeAsync(series.LibraryId, ct);
var chapter = volume.Chapters.FirstOrDefault(c => c.Id == chapterId);
if (chapter == null)
{
throw new OpdsException(await localizationService.TranslateAsync(userId, "chapter-doesnt-exist"));
}
var namingContext = await LocalizedNamingContext.CreateAsync(namingService, localizationService, userId, libraryType);
var chapterName = namingContext.FormatChapterTitle(chapter);
var feed = CreateFeed( $"{series.Name} - Volume {volume.Name} - {chapterName} {chapterId}",
$"{apiKey}/series/{seriesId}/volume/{volumeId}/chapter/{chapterId}", apiKey, prefix);
SetFeedId(feed, $"series-{series.Id}-volume-{volumeId}-{chapterId}-files");
feed.Entries.Add(CreateChapterFeedEntry(series, volume, chapter, namingContext, request));
return feed;
}
public async Task<Feed> Search(OpdsSearchRequest request, CancellationToken ct = default)
{
var userId = UnpackRequest(request, out var apiKey, out var prefix, out var baseUrl);View on GitHub (pinned to 9c3e540000)
Solutions
- Navigate back to the volume feed in the OPDS client to get current chapter IDs.
- Rescan the library if chapters were restructured.
- Verify the chapterId belongs to the specified volumeId.
- Refresh the OPDS client's cache after library rescans.
- Check Kavita's UI to confirm the chapter exists under the expected volume.
Example fix
// No code fix — chapter not in volume.
// OPDS client: go back to the volume feed and re-select the
// chapter to get a current URL.
// Verify chapterId is within volumeId's chapters:
// GET {apiKey}/series/{seriesId}/volume/{volumeId}
// -> list of valid chapter IDs in that volume Defensive patterns
Strategy: validation
Validate before calling
// Before requesting a chapter feed, verify the full chain:
// var series = await unitOfWork.SeriesRepository.GetSeriesDtoByIdAsync(seriesId, userId, ct);
// if (series == null) { ShowUser("Series not found."); return; }
// var volume = await unitOfWork.VolumeRepository.GetVolumeDtoAsync(volumeId, userId, ct);
// if (volume == null) { ShowUser("Volume not found."); return; }
// if (volume.Chapters.All(c => c.Id != chapterId))
// {
// ShowUser("Chapter not found in this volume.");
// return;
// }
// var feed = await opdsService.GetItemsFromChapter(request, ct); Try / catch
// try { var feed = await opdsService.GetItemsFromChapter(request, ct); }
// catch (OpdsException ex) when (ex.Message.Contains("chapter-doesnt-exist"))
// {
// // Chapter not in this volume (moved or deleted during rescan).
// await RefreshVolumeFeed(seriesId, volumeId);
// return BadRequest(ex.Message);
// } Prevention
- Verify the chapterId belongs to the specified volumeId before requesting the feed.
- Navigate from volume -> chapter to ensure the chapter is current.
- Refresh the OPDS feed after library rescans that restructure chapters.
- Avoid caching deep OPDS URLs across library rescans.
- Handle 'chapter-doesnt-exist' by refreshing the volume feed.
When it happens
Trigger: An OPDS client requests {apiKey}/series/{seriesId}/volume/{volumeId}/chapter/{chapterId}. The series and volume are valid and accessible, but the chapterId doesn't match any chapter in volume.Chapters. This can happen if the chapter was moved to a different volume during rescan, deleted, or the chapterId is from a different volume.
Common situations: A library rescan restructured chapters (e.g., a chapter moved from Volume 1 to Volume 2). The chapterId is from a different volume than volumeId. The chapter was a special or extra that got re-categorized. The OPDS reader cached an old URL where the chapter-volume mapping changed.
Related errors
- volume-doesnt-exist
- smart-filter-doesnt-exist
- series-doesnt-exist
- collection-doesnt-exist
- reading-list-restricted
AI-assisted analysis of Kareadita/Kavita@9c3e540000 (2026-08-13).
Data as JSON: /api/errors/85d290ae88044eb3.
Report an issue: GitHub.