{"record":{"id":"5f4945984429cd4d","repo":"can1357/oh-my-pi","slug":"invalid-xml-unterminated-comment","errorCode":null,"errorMessage":"Invalid XML: unterminated comment","messagePattern":"Invalid XML: unterminated comment","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/utils/src/docx/xml.ts","lineNumber":71,"sourceCode":"\t\tattributes: new Map(),\n\t\tchildren: [],\n\t};\n\tconst stack: Array<{ name: string; attributes: Map<string, string>; children: XmlNode[] }> = [synthetic];\n\tlet offset = 0;\n\twhile (offset < source.length) {\n\t\tconst lessThan = source.indexOf(\"<\", offset);\n\t\tif (lessThan === -1) {\n\t\t\tconst value = decodeEntities(source.slice(offset));\n\t\t\tif (value) stack[stack.length - 1].children.push({ kind: \"text\", value });\n\t\t\tbreak;\n\t\t}\n\t\tif (lessThan > offset) {\n\t\t\tconst value = decodeEntities(source.slice(offset, lessThan));\n\t\t\tif (value) stack[stack.length - 1].children.push({ kind: \"text\", value });\n\t\t}\n\t\tif (source.startsWith(\"<!--\", lessThan)) {\n\t\t\tconst end = source.indexOf(\"-->\", lessThan + 4);\n\t\t\tif (end === -1) throw new Error(\"Invalid XML: unterminated comment\");\n\t\t\toffset = end + 3;\n\t\t\tcontinue;\n\t\t}\n\t\tif (source.startsWith(\"<![CDATA[\", lessThan)) {\n\t\t\tconst end = source.indexOf(\"]]>\", lessThan + 9);\n\t\t\tif (end === -1) throw new Error(\"Invalid XML: unterminated CDATA section\");\n\t\t\tstack[stack.length - 1].children.push({ kind: \"text\", value: source.slice(lessThan + 9, end) });\n\t\t\toffset = end + 3;\n\t\t\tcontinue;\n\t\t}\n\t\tif (source.startsWith(\"<?\", lessThan)) {\n\t\t\tconst end = source.indexOf(\"?>\", lessThan + 2);\n\t\t\tif (end === -1) throw new Error(\"Invalid XML: unterminated processing instruction\");\n\t\t\toffset = end + 2;\n\t\t\tcontinue;\n\t\t}\n\t\tif (source.startsWith(\"<!\", lessThan)) {\n\t\t\tconst end = source.indexOf(\">\", lessThan + 2);","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/docx/xml.ts#L53-L89","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Fix the XML source so every \"<!--\" has a matching \"-->\".","Check for truncation: compare file/entry sizes or re-extract the DOCX; re-download the file if cut off.","If building XML with templates, validate comment delimiters before serializing (e.g. escape or remove user data containing \"<!--\").","Pre-sanitize inputs: strip unterminated comments or reject documents where document.xml fails a well-formedness check."],"exampleFix":"// before\nconst xml = `<doc><!-- broken`;\nparseXml(xml); // throws\n\n// after\nconst xml = `<doc><!-- ok --></doc>`;\nparseXml(xml);","handlingStrategy":"validation","validationCode":"function hasUnterminatedComment(xml: string): boolean {\n  return (xml.match(/<!--/g)?.length ?? 0) > (xml.match(/-->/g)?.length ?? 0);\n}\nif (hasUnterminatedComment(xml)) throw new Error(\"XML has an unterminated comment\");","typeGuard":null,"tryCatchPattern":"try {\n  const doc = parseXml(xml);\n} catch (err) {\n  if (err instanceof Error && err.message === \"Invalid XML: unterminated comment\") {\n    // strip the dangling comment tail or reject the document\n    xml = xml.slice(0, xml.lastIndexOf(\"<!--\"));\n  } else throw err;\n}","preventionTips":["Escape or strip \"<!--\" sequences from user-supplied data embedded in XML.","When building XML from templates, emit comments as complete pairs.","Check that generated/transported XML isn't truncated (size or checksum verification).","Validate XML well-formedness before conversion to get precise diagnostics."],"tags":["xml","parsing","docx","malformed-input"],"backgroundTag":"malformed-xml","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}