Kareadita/Kavita · error · KavitaException
chapter-doesnt-exist
chapter-doesnt-exist
Error message
chapter-doesnt-exist
What it means
Thrown by AnnotationService.CreateAnnotation when ChapterRepository.GetChapterAsync(dto.ChapterId) returns null — i.e. no Chapter row exists for the supplied ChapterId. Like error 20, this throw lives inside the outer try and is caught/re-wrapped as 'annotation-failed-create' (line 98 catch), so the caller never sees 'chapter-doesnt-exist' directly. The distinct code is reachable but masked; check server logs for the underlying exception.
Source
Thrown at Kavita.Services/AnnotationService.cs:55
/// <summary>
/// Create a new Annotation for the user against a Chapter
/// </summary>
/// <param name="userId"></param>
/// <param name="dto"></param>
/// <param name="ct"></param>
/// <returns></returns>
/// <exception cref="KavitaException">Message is not localized</exception>
public async Task<AnnotationDto> CreateAnnotation(int userId, AnnotationDto dto, CancellationToken ct = default)
{
try
{
if (dto.HighlightCount == 0 || string.IsNullOrWhiteSpace(dto.SelectedText))
{
throw new KavitaException("invalid-payload");
}
var chapter = await unitOfWork.ChapterRepository.GetChapterAsync(dto.ChapterId, ct: ct) ?? throw new KavitaException("chapter-doesnt-exist");
var chapterTitle = string.Empty;
try
{
var toc = await bookService.GenerateTableOfContents(chapter);
var pageTocs = BookChapterItemHelper.GetTocForPage(toc, dto.PageNumber);
if (pageTocs.Count > 0)
{
chapterTitle = pageTocs[0].Title;
}
}
catch (KavitaException)
{
/* Swallow */
}
var annotation = new AppUserAnnotation()
{View on GitHub (pinned to 9c3e540000)
Solutions
- Have the client refresh chapter metadata before creating an annotation when the reader has been open a long time.
- Validate dto.ChapterId against the user's accessible chapters in the controller/service before the create (return 404/400 explicitly).
- Stop masking the exception: catch KavitaException separately and rethrow so 'chapter-doesnt-exist' propagates to the API instead of 'annotation-failed-create'.
Example fix
// before
var chapter = await unitOfWork.ChapterRepository.GetChapterAsync(dto.ChapterId, ct: ct)
?? throw new KavitaException("chapter-doesnt-exist");
// ...this is inside try{} and caught by catch(Exception) -> rethrown as annotation-failed-create
// after — move the lookup before the outer try, or rethrow KavitaException
catch (KavitaException) { throw; }
catch (Exception ex) { logger.LogError(ex, ...); throw new KavitaException("annotation-failed-create"); } Defensive patterns
Strategy: validation
Validate before calling
// Confirm the chapter exists and is accessible before creating
var exists = await unitOfWork.ChapterRepository.GetChapterAsync(dto.ChapterId, ct: ct);
if (exists is null) return NotFound($"Chapter {dto.ChapterId} not found"); Prevention
- Refresh chapter ids on the client after a library rescan before allowing annotation creation.
- Keep the reader's chapter context fresh; don't persist ChapterId across long sessions.
- Note that 'chapter-doesnt-exist' is masked into 'annotation-failed-create' — verify via logs.
When it happens
Trigger: POST /api/annotation/create referencing a ChapterId that was deleted, never existed, or is out of range; stale client state holding a ChapterId from a removed series; a typo/manipulated id in the request body.
Common situations: Library was rescanned and chapters were merged/removed while the book reader was open; the chapter belongs to a library the user no longer has access to (the dto is constructed client-side from cached ids).
Related errors
AI-assisted analysis of Kareadita/Kavita@9c3e540000 (2026-08-13).
Data as JSON: /api/errors/1c433c53b39a4953.
Report an issue: GitHub.