{"record":{"id":"223c33306513695f","repo":"Kareadita/Kavita","slug":"image-index-bookmarkdto-imageoffset-is-out-of-ra","errorCode":null,"errorMessage":"Image index {bookmarkDto.ImageOffset} is out of range. Page has {images.Count} images","messagePattern":"Image index (.+?) is out of range\\. Page has (.+?) images","errorType":"exception","errorClass":"KavitaException","httpStatus":null,"severity":"warning","filePath":"Kavita.Services/BookService.cs","lineNumber":1258,"sourceCode":"            {\n                counter++;\n                continue;\n            }\n\n            var content = await contentFileRef.ReadContentAsync();\n            doc.LoadHtml(content);\n\n            var images = doc.DocumentNode.SelectNodes(\"//img\")\n                         ?? doc.DocumentNode.SelectNodes(\"//image\");\n\n            if (images == null || images.Count == 0)\n            {\n                throw new KavitaException(\"No images found on the specified page\");\n            }\n\n            if (bookmarkDto.ImageOffset >= images.Count)\n            {\n                throw new KavitaException($\"Image index {bookmarkDto.ImageOffset} is out of range. Page has {images.Count} images\");\n            }\n\n            var targetImage = images[bookmarkDto.ImageOffset];\n\n            // Get the image source attribute\n            var srcAttributeName = GetImageSrcAttributeName(targetImage);\n\n            if (string.IsNullOrEmpty(srcAttributeName))\n            {\n                throw new KavitaException(\"Image element does not have a valid source attribute\");\n            }\n\n            var imageSource = targetImage.Attributes[srcAttributeName].Value;\n\n            // Clean and get the correct key for the image\n            var imageKey = CleanContentKeys(GetKeyForImage(book, imageSource));\n\n            // Check if it's an external URL","sourceCodeStart":1240,"sourceCodeEnd":1276,"githubUrl":"https://github.com/Kareadita/Kavita/blob/9c3e5400007f8a0282f7d883f2ad5e71716e514d/Kavita.Services/BookService.cs#L1240-L1276","documentation":"Thrown by BookService.CopyImageToTempFromBook (line 1258) when bookmarkDto.ImageOffset is >= the number of images on the target page (zero-based index out of range). The interpolated message reports both the requested offset and the actual image count.","triggerScenarios":"Bookmark/copy-image request with ImageOffset pointing past the last image, e.g. offset 2 on a page with 1 image, or offset computed before images were added/removed.","commonSituations":"Client reused a stale ImageOffset from a previous render; off-by-one where the client treats the offset as 1-based.","solutions":["Clamp ImageOffset to [0, imageCount-1] before requesting; treat it as zero-based.","Re-query the page's image count when the reader reloads and invalidate stale offsets."],"exampleFix":"// before\nif (bookmarkDto.ImageOffset >= images.Count)\n    throw new KavitaException($\"Image index {bookmarkDto.ImageOffset} is out of range. Page has {images.Count} images\");\n\n// caller\nvar safeOffset = Math.Clamp(bookmarkDto.ImageOffset, 0, Math.Max(0, knownImageCount - 1));","handlingStrategy":"validation","validationCode":"// Treat ImageOffset as zero-based and clamp before the request\nbookmarkDto.ImageOffset = Math.Clamp(bookmarkDto.ImageOffset, 0, Math.Max(0, pageImageCount - 1));","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use a consistent zero-based offset convention between client and service.","Invalidate cached offsets when the page's image set changes."],"tags":["book-reader","epub","bookmark","validation","index-out-of-range"],"backgroundTag":null,"analyzedSha":"9c3e5400007f8a0282f7d883f2ad5e71716e514d","analyzedAt":"2026-08-13T19:06:05.897Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}