{"record":{"id":"1032099a734e39fa","repo":"can1357/oh-my-pi","slug":"invalid-epub-missing-rootfile-path","errorCode":null,"errorMessage":"Invalid EPUB: missing rootfile path","messagePattern":"Invalid EPUB: missing rootfile path","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/markit/converters/epub.ts","lineNumber":69,"sourceCode":"\t\tif (streamInfo.mimetype && MIMETYPES.some(m => streamInfo.mimetype?.startsWith(m))) return true;\n\t\treturn false;\n\t}\n\n\tasync convert(input: Buffer, _streamInfo: StreamInfo): Promise<ConversionResult> {\n\t\tconst entries = await readArchiveEntries({ bytes: input, format: \"zip\" });\n\t\tconst parser = new XMLParser({\n\t\t\tignoreAttributes: false,\n\t\t\tattributeNamePrefix: \"@_\",\n\t\t\ttextNodeName: \"#text\",\n\t\t\tprocessEntities: { maxTotalExpansions: 1_000_000 },\n\t\t});\n\t\t// Find content.opf path from container.xml\n\t\tconst containerXml = archiveEntryText(entries, \"META-INF/container.xml\");\n\t\tif (!containerXml) throw new Error(\"Invalid EPUB: missing container.xml\");\n\t\tconst container = parser.parse(containerXml) as ContainerDoc;\n\t\tconst rootfile = container.container?.rootfiles?.rootfile;\n\t\tconst opfPath = Array.isArray(rootfile) ? rootfile[0][\"@_full-path\"] : rootfile?.[\"@_full-path\"];\n\t\tif (!opfPath) throw new Error(\"Invalid EPUB: missing rootfile path\");\n\t\t// Parse content.opf\n\t\tconst opfXml = archiveEntryText(entries, opfPath);\n\t\tif (!opfXml) throw new Error(\"Invalid EPUB: missing content.opf\");\n\t\tconst opf = parser.parse(opfXml) as OpfDoc;\n\t\t// Extract metadata\n\t\tconst meta: Metadata = opf.package?.metadata ?? {};\n\t\tconst metadata: Record<string, string | undefined> = {\n\t\t\ttitle: this.getText(meta[\"dc:title\"]),\n\t\t\tauthors: this.getTextArray(meta[\"dc:creator\"]).join(\", \") || undefined,\n\t\t\tlanguage: this.getText(meta[\"dc:language\"]),\n\t\t\tpublisher: this.getText(meta[\"dc:publisher\"]),\n\t\t\tdate: this.getText(meta[\"dc:date\"]),\n\t\t\tdescription: this.getText(meta[\"dc:description\"]),\n\t\t};\n\t\t// Build manifest map (id → href)\n\t\tconst manifestItems = opf.package?.manifest?.item;\n\t\tconst itemList = Array.isArray(manifestItems) ? manifestItems : manifestItems ? [manifestItems] : [];\n\t\tconst manifest = new Map<string, string>();","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/markit/converters/epub.ts#L51-L87","documentation":"After parsing META-INF/container.xml, the converter extracts the rootfile's full-path attribute, which points at the package document (content.opf). If container.xml parses but contains no rootfile element or an empty full-path, convert() throws this error: the container declares no usable package document.","triggerScenarios":"container.xml exists but is empty, malformed (so container.container?.rootfiles?.rootfile is undefined), or its <rootfile> lacks the full-path attribute.","commonSituations":"Hand-edited or generator-broken container.xml; namespace/structure mismatch confusing the XML parser into a different shape; EPUB produced by a buggy tooling pipeline that wrote an empty rootfiles element.","solutions":["Inspect META-INF/container.xml and ensure it has <rootfiles><rootfile full-path=\"OEBPS/content.opf\" media-type=\"application/oebps-package+xml\"/></rootfiles>.","Validate the EPUB with epubcheck and fix reported container issues.","Re-export/re-download the EPUB from a known-good source.","If you control generation, fix the tool writing container.xml to emit the rootfile element."],"exampleFix":"// before (broken container.xml)\n<container version=\"1.0\" xmlns=\"urn:oasis:names:tc:opendocument:xmlns:container\"><rootfiles/></container>\n// after\n<container version=\"1.0\" xmlns=\"urn:oasis:names:tc:opendocument:xmlns:container\"><rootfiles><rootfile full-path=\"OEBPS/content.opf\" media-type=\"application/oebps-package+xml\"/></rootfiles></container>","handlingStrategy":"validation","validationCode":"async function hasEpubRootfile(path: string, listEntries: (p: string) => Promise<string[]>, readEntry: (p: string, name: string) => Promise<string>): Promise<boolean> {\n  const containerXml = await readEntry(path, \"META-INF/container.xml\");\n  if (!containerXml?.includes(\"<rootfile\")) return false;\n  const full = /full-path=\"([^\"]+)\"/.exec(containerXml)?.[1];\n  if (!full) return false;\n  return (await listEntries(path)).includes(full);\n}","typeGuard":null,"tryCatchPattern":"try {\n  const result = await markit.convertFile(\"book.epub\");\n} catch (err) {\n  if (err instanceof Error && err.message === \"Invalid EPUB: missing rootfile path\") {\n    throw new Error(\"book.epub container.xml has no <rootfile full-path>; rebuild the EPUB\");\n  }\n  throw err;\n}","preventionTips":["Validate container.xml structure with epubcheck.","Regenerate EPUBs with maintained tooling instead of hand-editing XML.","Assert the rootfile element exists in any EPUB you generate.","Keep namespaces intact when post-processing EPUB XML."],"tags":["epub","file-format","xml","markitdown"],"backgroundTag":"invalid-epub-container","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}