can1357/oh-my-pi · error · Error

Invalid XML: unterminated comment

Error message

Invalid XML: unterminated comment

What it means

The minimal XML parser used for DOCX parts scans for "<!--" comment starts and requires a closing "-->". If the source contains a comment opener with no closer before end-of-input, parsing cannot continue and this error is thrown from parseXml.

Source

Thrown at packages/utils/src/docx/xml.ts:71

		attributes: new Map(),
		children: [],
	};
	const stack: Array<{ name: string; attributes: Map<string, string>; children: XmlNode[] }> = [synthetic];
	let offset = 0;
	while (offset < source.length) {
		const lessThan = source.indexOf("<", offset);
		if (lessThan === -1) {
			const value = decodeEntities(source.slice(offset));
			if (value) stack[stack.length - 1].children.push({ kind: "text", value });
			break;
		}
		if (lessThan > offset) {
			const value = decodeEntities(source.slice(offset, lessThan));
			if (value) stack[stack.length - 1].children.push({ kind: "text", value });
		}
		if (source.startsWith("<!--", lessThan)) {
			const end = source.indexOf("-->", lessThan + 4);
			if (end === -1) throw new Error("Invalid XML: unterminated comment");
			offset = end + 3;
			continue;
		}
		if (source.startsWith("<![CDATA[", lessThan)) {
			const end = source.indexOf("]]>", lessThan + 9);
			if (end === -1) throw new Error("Invalid XML: unterminated CDATA section");
			stack[stack.length - 1].children.push({ kind: "text", value: source.slice(lessThan + 9, end) });
			offset = end + 3;
			continue;
		}
		if (source.startsWith("<?", lessThan)) {
			const end = source.indexOf("?>", lessThan + 2);
			if (end === -1) throw new Error("Invalid XML: unterminated processing instruction");
			offset = end + 2;
			continue;
		}
		if (source.startsWith("<!", lessThan)) {
			const end = source.indexOf(">", lessThan + 2);

View on GitHub (pinned to 9690622007)

Solutions

  1. Fix the XML source so every "<!--" has a matching "-->".
  2. Check for truncation: compare file/entry sizes or re-extract the DOCX; re-download the file if cut off.
  3. If building XML with templates, validate comment delimiters before serializing (e.g. escape or remove user data containing "<!--").
  4. Pre-sanitize inputs: strip unterminated comments or reject documents where document.xml fails a well-formedness check.

Example fix

// before
const xml = `<doc><!-- broken`;
parseXml(xml); // throws

// after
const xml = `<doc><!-- ok --></doc>`;
parseXml(xml);
Defensive patterns

Strategy: validation

Validate before calling

function hasUnterminatedComment(xml: string): boolean {
  return (xml.match(/<!--/g)?.length ?? 0) > (xml.match(/-->/g)?.length ?? 0);
}
if (hasUnterminatedComment(xml)) throw new Error("XML has an unterminated comment");

Try / catch

try {
  const doc = parseXml(xml);
} catch (err) {
  if (err instanceof Error && err.message === "Invalid XML: unterminated comment") {
    // strip the dangling comment tail or reject the document
    xml = xml.slice(0, xml.lastIndexOf("<!--"));
  } else throw err;
}

Prevention

When it happens

Trigger: Calling parseXml() (directly or via convertToHtml on a DOCX part) with XML containing "<!--" that is never closed — a truncated file, generated XML with an unclosed comment, or text pasted mid-comment.

Common situations: Template generation that writes comment headers without closing them; string slicing/regex edits of XML that cut a comment in half; partially downloaded or truncated document.xml/styles.xml inside the DOCX.

Related errors


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