can1357/oh-my-pi · error · Error
Invalid XML: unterminated tag
Error message
Invalid XML: unterminated tag
What it means
Regular tags must be closed with ">". When the parser finds a "<" that starts neither a comment, CDATA, processing instruction, nor declaration, and no ">" follows before end-of-input, it throws this error. The parser then continues to tag matching, where an unexpected closing tag also throws ("Invalid XML: unexpected closing tag").
Source
Thrown at packages/utils/src/docx/xml.ts:95
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);
if (end === -1) throw new Error("Invalid XML: unterminated declaration");
offset = end + 1;
continue;
}
const end = source.indexOf(">", lessThan + 1);
if (end === -1) throw new Error("Invalid XML: unterminated tag");
const raw = source.slice(lessThan + 1, end).trim();
if (raw.startsWith("/")) {
if (stack.length === 1) throw new Error("Invalid XML: unexpected closing tag");
const closingName = raw.slice(1).trim();
const completed = stack.pop();
if (!completed || completed.name !== closingName)
throw new Error(`Invalid XML: mismatched closing tag ${closingName}`);
stack[stack.length - 1].children.push({
kind: "element",
name: completed.name,
attributes: completed.attributes,
children: completed.children,
});
} else {
const selfClosing = raw.endsWith("/");
const tag = selfClosing ? raw.slice(0, -1).trim() : raw;
const whitespace = tag.search(/\s/);
const name = whitespace === -1 ? tag : tag.slice(0, whitespace);View on GitHub (pinned to 9690622007)
Solutions
- Escape literal "<" in text content as "<" (and "&" as "&") before embedding data in XML.
- Ensure every opened tag is complete: check the XML ends cleanly with matching close tags.
- Check for truncation — compare entry sizes or re-download/re-extract the file.
- Fix the XML-generating code to serialize complete elements and validate output before conversion.
Example fix
// before const xml = `<doc><text>5 < 10`; // raw < and unclosed tag // after const xml = `<doc><text>5 < 10</text></doc>`; parseXml(xml);
Defensive patterns
Strategy: validation
Validate before calling
function escapeXmlText(s: string): string {
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
}
const xml = `<doc><text>${escapeXmlText(userText)}</text></doc>`;
if (!xml.trim().endsWith(">") || (xml.match(/</g)?.length ?? 0) !== (xml.match(/>/g)?.length ?? 0)) {
throw new Error("XML has unbalanced angle brackets");
} Try / catch
try {
const doc = parseXml(xml);
} catch (err) {
if (err instanceof Error && err.message === "Invalid XML: unterminated tag") {
// reject the input or repair truncation before retrying
} else throw err;
} Prevention
- Always escape raw text data (escapeXmlText) before embedding it in XML.
- Ensure every opened tag is closed; validate output of your XML generator.
- Check for truncation in files pulled from network or archives.
- Add a well-formedness pre-check with a strict parser for untrusted inputs.
When it happens
Trigger: Calling parseXml() on XML with an unclosed tag — input truncated mid-element (e.g. "<w:p"), a stray "<" in text that wasn't escaped, or generated XML missing the final ">".
Common situations: Raw "<" or "&" characters in document text without XML escaping; truncated downloads or archive entries; template bugs dropping the closing bracket of the last element.
Related errors
- Invalid XML: unterminated comment
- Invalid XML: unterminated CDATA section
- Invalid XML: unterminated processing instruction
- Invalid XML: unterminated declaration
- Invalid XML: unexpected closing tag
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/e7ea580e711a9b1f.
Report an issue: GitHub.