can1357/oh-my-pi · error · Error

Invalid EPUB: missing container.xml

Error message

Invalid EPUB: missing container.xml

What it means

The EPUB converter requires the ZIP archive to contain META-INF/container.xml, the OCF file that maps the publication to its content.opf. If archiveEntryText finds no such entry, convert() throws this error, meaning the file is not a valid OCF EPUB container.

Source

Thrown at packages/coding-agent/src/markit/converters/epub.ts:65

	name = "epub";

	accepts(streamInfo: StreamInfo): boolean {
		if (streamInfo.extension && EXTENSIONS.includes(streamInfo.extension)) return true;
		if (streamInfo.mimetype && MIMETYPES.some(m => streamInfo.mimetype?.startsWith(m))) return true;
		return false;
	}

	async convert(input: Buffer, _streamInfo: StreamInfo): Promise<ConversionResult> {
		const entries = await readArchiveEntries({ bytes: input, format: "zip" });
		const parser = new XMLParser({
			ignoreAttributes: false,
			attributeNamePrefix: "@_",
			textNodeName: "#text",
			processEntities: { maxTotalExpansions: 1_000_000 },
		});
		// Find content.opf path from container.xml
		const containerXml = archiveEntryText(entries, "META-INF/container.xml");
		if (!containerXml) throw new Error("Invalid EPUB: missing container.xml");
		const container = parser.parse(containerXml) as ContainerDoc;
		const rootfile = container.container?.rootfiles?.rootfile;
		const opfPath = Array.isArray(rootfile) ? rootfile[0]["@_full-path"] : rootfile?.["@_full-path"];
		if (!opfPath) throw new Error("Invalid EPUB: missing rootfile path");
		// Parse content.opf
		const opfXml = archiveEntryText(entries, opfPath);
		if (!opfXml) throw new Error("Invalid EPUB: missing content.opf");
		const opf = parser.parse(opfXml) as OpfDoc;
		// Extract metadata
		const meta: Metadata = opf.package?.metadata ?? {};
		const metadata: Record<string, string | undefined> = {
			title: this.getText(meta["dc:title"]),
			authors: this.getTextArray(meta["dc:creator"]).join(", ") || undefined,
			language: this.getText(meta["dc:language"]),
			publisher: this.getText(meta["dc:publisher"]),
			date: this.getText(meta["dc:date"]),
			description: this.getText(meta["dc:description"]),
		};

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify the file is a real EPUB: unzip -l book.epub should list META-INF/container.xml.
  2. Obtain a proper EPUB from the source; re-download or re-export it.
  3. If the file is a plain ZIP/HTML, convert it with the appropriate converter instead of the EPUB one.
  4. Re-zip with an EPUB-compliant tool (epubcheck-validated output) if you built it yourself.

Example fix

// before
await markit.convert(file, "application/zip"); // mislabeled non-EPUB
// after
await markit.convert(file, "application/epub+zip"); // and ensure the EPUB is valid (epubcheck)
Defensive patterns

Strategy: validation

Validate before calling

async function hasEpubContainer(path: string, listEntries: (p: string) => Promise<string[]>): Promise<boolean> {
  const names = await listEntries(path);
  return names.includes("META-INF/container.xml");
}

Try / catch

try {
  const result = await markit.convertFile("book.epub");
} catch (err) {
  if (err instanceof Error && /Invalid EPUB: missing (container\.xml|rootfile path|content\.opf)/.test(err.message)) {
    throw new Error("book.epub is not a valid EPUB (OCF container incomplete); re-export or validate with epubcheck");
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling the EPUB converter (via Markit.convert/convertFile) on a file whose ZIP lacks META-INF/container.xml at that exact path.

Common situations: File is not actually an EPUB (renamed HTML/ZIP/PDF); EPUB produced by a tool that omitted the META-INF directory; archive was rebuilt/re-zipped incorrectly losing the container; file corrupted or truncated so the entry is missing.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/624c442ff2306bfd. Report an issue: GitHub.