{"record":{"id":"e7ea580e711a9b1f","repo":"can1357/oh-my-pi","slug":"invalid-xml-unterminated-tag","errorCode":null,"errorMessage":"Invalid XML: unterminated tag","messagePattern":"Invalid XML: unterminated tag","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/utils/src/docx/xml.ts","lineNumber":95,"sourceCode":"\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);\n\t\t\tif (end === -1) throw new Error(\"Invalid XML: unterminated declaration\");\n\t\t\toffset = end + 1;\n\t\t\tcontinue;\n\t\t}\n\t\tconst end = source.indexOf(\">\", lessThan + 1);\n\t\tif (end === -1) throw new Error(\"Invalid XML: unterminated tag\");\n\t\tconst raw = source.slice(lessThan + 1, end).trim();\n\t\tif (raw.startsWith(\"/\")) {\n\t\t\tif (stack.length === 1) throw new Error(\"Invalid XML: unexpected closing tag\");\n\t\t\tconst closingName = raw.slice(1).trim();\n\t\t\tconst completed = stack.pop();\n\t\t\tif (!completed || completed.name !== closingName)\n\t\t\t\tthrow new Error(`Invalid XML: mismatched closing tag ${closingName}`);\n\t\t\tstack[stack.length - 1].children.push({\n\t\t\t\tkind: \"element\",\n\t\t\t\tname: completed.name,\n\t\t\t\tattributes: completed.attributes,\n\t\t\t\tchildren: completed.children,\n\t\t\t});\n\t\t} else {\n\t\t\tconst selfClosing = raw.endsWith(\"/\");\n\t\t\tconst tag = selfClosing ? raw.slice(0, -1).trim() : raw;\n\t\t\tconst whitespace = tag.search(/\\s/);\n\t\t\tconst name = whitespace === -1 ? tag : tag.slice(0, whitespace);","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/docx/xml.ts#L77-L113","documentation":"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\").","triggerScenarios":"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 \">\".","commonSituations":"Raw \"<\" or \"&\" characters in document text without XML escaping; truncated downloads or archive entries; template bugs dropping the closing bracket of the last element.","solutions":["Escape literal \"<\" in text content as \"&lt;\" (and \"&\" as \"&amp;\") 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."],"exampleFix":"// before\nconst xml = `<doc><text>5 < 10`; // raw < and unclosed tag\n\n// after\nconst xml = `<doc><text>5 &lt; 10</text></doc>`;\nparseXml(xml);","handlingStrategy":"validation","validationCode":"function escapeXmlText(s: string): string {\n  return s.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\");\n}\nconst xml = `<doc><text>${escapeXmlText(userText)}</text></doc>`;\nif (!xml.trim().endsWith(\">\") || (xml.match(/</g)?.length ?? 0) !== (xml.match(/>/g)?.length ?? 0)) {\n  throw new Error(\"XML has unbalanced angle brackets\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  const doc = parseXml(xml);\n} catch (err) {\n  if (err instanceof Error && err.message === \"Invalid XML: unterminated tag\") {\n    // reject the input or repair truncation before retrying\n  } else throw err;\n}","preventionTips":["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."],"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"}