{"record":{"id":"8262eff00eede529","repo":"Kareadita/Kavita","slug":"epub-malformed","errorCode":null,"errorMessage":"epub-malformed","messagePattern":"epub-malformed","errorType":"exception","errorClass":"KavitaException","httpStatus":null,"severity":"error","filePath":"Kavita.Services/BookService.cs","lineNumber":1792,"sourceCode":"\n                var content = await contentFileRef.ReadContentAsync();\n                if (contentFileRef.ContentType != EpubContentType.XHTML_1_1) return content;\n\n                // In more cases than not, due to this being XML not HTML, we need to escape the script tags.\n                content = EscapeTags(content);\n\n                doc.LoadHtml(content);\n\n\n                var body = doc.DocumentNode.SelectSingleNode(\"//body\");\n\n                // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract\n                if (body == null)\n                {\n                    if (doc.ParseErrors.Any())\n                    {\n                        LogBookErrors(book, contentFileRef, doc);\n                        throw new KavitaException(\"epub-malformed\");\n                    }\n                    logger.LogError(\"{FilePath} has no body tag! Generating one for support. Book may be skewed\", book.FilePath);\n                    doc.DocumentNode.SelectSingleNode(\"/html\").AppendChild(HtmlNode.CreateNode(\"<body></body>\"));\n                    body = doc.DocumentNode.SelectSingleNode(\"/html/body\");\n                }\n\n                return await ScopePage(doc, book, apiBase, body!, mappings, page, ptocBookmarks, annotations, ct);\n            }\n        } catch (Exception ex)\n        {\n            logger.LogError(ex, \"There was an issue reading one of the pages for {Book}\", book.FilePath);\n            await mediaErrorService.ReportMediaIssueAsync(book.FilePath ?? string.Empty, MediaErrorProducer.BookService,\n                \"There was an issue reading one of the pages for\", ex, ct);\n        }\n\n        throw new KavitaException(\"epub-html-missing\");\n    }\n","sourceCodeStart":1774,"sourceCodeEnd":1810,"githubUrl":"https://github.com/Kareadita/Kavita/blob/9c3e5400007f8a0282f7d883f2ad5e71716e514d/Kavita.Services/BookService.cs#L1774-L1810","documentation":"Thrown by BookService (ScopePage/GetBookPage path, line 1792) when an XHTML page has no <body> node AND HtmlDocument reports ParseErrors — i.e. the page's XHTML is malformed enough that the parser cannot find a body and also recorded parse errors. This is the hard-fail branch of book-page rendering; a missing body WITHOUT parse errors is auto-healed by synthesizing a <body>. The original parse errors are logged via LogBookErrors first.","triggerScenarios":"Rendering an epub page (book reader) where the XHTML is malformed: unclosed tags, invalid entities, broken XML structure producing parse errors and no recoverable body.","commonSituations":"Poorly authored or badly converted epub; truncation of the XHTML inside the archive; non-XML-conformant HTML the lenient options cannot salvage.","solutions":["Run epubcheck and repair the offending XHTML file in the epub.","Re-export/re-convert the epub with a tool that emits well-formed XHTML.","If broad compatibility is needed, strengthen the lenient parse options or pre-sanitize the XHTML before LoadHtml."],"exampleFix":"// before\nif (body == null) {\n    if (doc.ParseErrors.Any()) {\n        LogBookErrors(book, contentFileRef, doc);\n        throw new KavitaException(\"epub-malformed\");\n    }\n    // auto-heal path...\n}\n\n// after — attempt a best-effort body synthesis even when parse errors exist, report but don't hard-fail\nif (body == null) {\n    LogBookErrors(book, contentFileRef, doc);\n    var html = doc.DocumentNode.SelectSingleNode(\"/html\");\n    if (html != null) { html.AppendChild(HtmlNode.CreateNode(\"<body></body>\")); body = doc.DocumentNode.SelectSingleNode(\"/html/body\"); }\n    if (body == null) throw new KavitaException(\"epub-malformed\");\n}","handlingStrategy":"validation","validationCode":"// At import/scan, reject or flag epubs whose pages fail to parse\nif (doc.ParseErrors.Any() && doc.DocumentNode.SelectSingleNode(\"//body\") == null)\n    mediaErrorService.ReportMediaIssue(path, MediaErrorProducer.BookService, \"malformed xhtml\", null);","typeGuard":null,"tryCatchPattern":"catch (KavitaException ex) when (ex.Message == \"epub-malformed\") { /* surface a friendly 'book is corrupted' to the reader */ }","preventionTips":["Run epubcheck on the library to find malformed books proactively.","Re-export epubs with a tool that emits well-formed XHTML.","Surface MediaError records so users know which book needs repair."],"tags":["book-reader","epub","corrupt-file","parsing","rendering"],"backgroundTag":null,"analyzedSha":"9c3e5400007f8a0282f7d883f2ad5e71716e514d","analyzedAt":"2026-08-13T19:06:05.897Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}