{"record":{"id":"e7a43dd5f6676924","repo":"can1357/oh-my-pi","slug":"invalid-epub-missing-content-opf","errorCode":null,"errorMessage":"Invalid EPUB: missing content.opf","messagePattern":"Invalid EPUB: missing content\\.opf","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/markit/converters/epub.ts","lineNumber":72,"sourceCode":"\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>();\n\t\tfor (const item of itemList) {\n\t\t\tmanifest.set(item[\"@_id\"], item[\"@_href\"]);\n\t\t}","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/markit/converters/epub.ts#L54-L90","documentation":"The converter resolved an opf path from container.xml but archiveEntryText found no entry at that path inside the ZIP. This means the package document (content.opf) referenced by the container is absent, so metadata and spine cannot be read and convert() aborts.","triggerScenarios":"container.xml's rootfile full-path names a file (e.g. OEBPS/content.opf) that does not exist in the archive — wrong path, case mismatch, or the OPF was deleted.","commonSituations":"EPUB re-zipped across case-sensitive/insensitive filesystems (Content.opf vs content.opf); partial extraction/repack dropped the OPF; custom directory layout with a stale container.xml pointing at an old OPF location.","solutions":["Compare the full-path in META-INF/container.xml against actual entry names (unzip -l); fix the path or rename the entry to match exactly (case-sensitive).","Rebuild the EPUB with a compliant packer (epubcheck-validated).","Re-download the EPUB; the archive is likely corrupt or partially transferred.","If repacking manually, include the OPF at the exact path the container references."],"exampleFix":"// before (container.xml says OEBPS/content.opf, archive has OEBPS/Content.opf)\n// after: rename entry to match\nzip book.epub OEBPS/content.opf  # exact case as full-path","handlingStrategy":"validation","validationCode":"async function opfEntryExists(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  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 content.opf\") {\n    throw new Error(\"book.epub references an OPF that is absent from the archive; fix full-path or repack\");\n  }\n  throw err;\n}","preventionTips":["Match container.xml full-path against actual zip entries (case-sensitive) before conversion.","Repack with EPUB-compliant tools so referenced parts are always included.","Validate with epubcheck after any archive modification.","Avoid filesystem case-mangling when moving EPUBs between OSes."],"tags":["epub","zip","file-format","markitdown"],"backgroundTag":"invalid-epub-container","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}