{"record":{"id":"1c433c53b39a4953","repo":"Kareadita/Kavita","slug":"chapter-doesnt-exist","errorCode":"chapter-doesnt-exist","errorMessage":"chapter-doesnt-exist","messagePattern":"chapter-doesnt-exist","errorType":"exception","errorClass":"KavitaException","httpStatus":400,"severity":"error","filePath":"Kavita.Services/AnnotationService.cs","lineNumber":55,"sourceCode":"\n    /// <summary>\n    /// Create a new Annotation for the user against a Chapter\n    /// </summary>\n    /// <param name=\"userId\"></param>\n    /// <param name=\"dto\"></param>\n    /// <param name=\"ct\"></param>\n    /// <returns></returns>\n    /// <exception cref=\"KavitaException\">Message is not localized</exception>\n    public async Task<AnnotationDto> CreateAnnotation(int userId, AnnotationDto dto, CancellationToken ct = default)\n    {\n        try\n        {\n            if (dto.HighlightCount == 0 || string.IsNullOrWhiteSpace(dto.SelectedText))\n            {\n                throw new KavitaException(\"invalid-payload\");\n            }\n\n            var chapter = await unitOfWork.ChapterRepository.GetChapterAsync(dto.ChapterId, ct: ct) ?? throw new KavitaException(\"chapter-doesnt-exist\");\n            var chapterTitle = string.Empty;\n\n            try\n            {\n                var toc = await bookService.GenerateTableOfContents(chapter);\n                var pageTocs = BookChapterItemHelper.GetTocForPage(toc, dto.PageNumber);\n                if (pageTocs.Count > 0)\n                {\n                    chapterTitle = pageTocs[0].Title;\n                }\n            }\n            catch (KavitaException)\n            {\n                /* Swallow */\n            }\n\n            var annotation = new AppUserAnnotation()\n            {","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/Kareadita/Kavita/blob/9c3e5400007f8a0282f7d883f2ad5e71716e514d/Kavita.Services/AnnotationService.cs#L37-L73","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","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'."],"exampleFix":"// before\nvar chapter = await unitOfWork.ChapterRepository.GetChapterAsync(dto.ChapterId, ct: ct)\n    ?? throw new KavitaException(\"chapter-doesnt-exist\");\n// ...this is inside try{} and caught by catch(Exception) -> rethrown as annotation-failed-create\n\n// after — move the lookup before the outer try, or rethrow KavitaException\ncatch (KavitaException) { throw; }\ncatch (Exception ex) { logger.LogError(ex, ...); throw new KavitaException(\"annotation-failed-create\"); }","handlingStrategy":"validation","validationCode":"// Confirm the chapter exists and is accessible before creating\nvar exists = await unitOfWork.ChapterRepository.GetChapterAsync(dto.ChapterId, ct: ct);\nif (exists is null) return NotFound($\"Chapter {dto.ChapterId} not found\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["annotation","data-not-found","book-reader","error-masking"],"backgroundTag":null,"analyzedSha":"9c3e5400007f8a0282f7d883f2ad5e71716e514d","analyzedAt":"2026-08-13T19:06:05.897Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}