can1357/oh-my-pi · error · Error

Invalid XML: unterminated processing instruction

Error message

Invalid XML: unterminated processing instruction

What it means

Processing instructions ("<?...?>", e.g. the XML declaration "<?xml ... ?>") must be terminated with "?>". The parser throws this error when it encounters "<?" with no closing "?>" before end-of-input.

Source

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

			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);
			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}`);

View on GitHub (pinned to 9690622007)

Solutions

  1. Add the missing "?>" terminator to the processing instruction/declaration in the source XML.
  2. Check for truncation and re-obtain the complete file or archive entry.
  3. Fix the XML-generating code so the declaration is emitted as one complete unit (e.g. `<?xml version="1.0" encoding="UTF-8"?>`).
  4. Run the XML through a validator before conversion to catch structural issues early.

Example fix

// before
const xml = `<?xml version="1.0"?><doc/>`; // ok, but truncated input below throws
const bad = `<?xml version="1.0"`;

// after
const fixed = bad + `?>` + `<doc/>`;
parseXml(fixed);
Defensive patterns

Strategy: validation

Validate before calling

function hasUnterminatedPI(xml: string): boolean {
  const opens = xml.match(/<\?/g)?.length ?? 0;
  const closes = xml.match(/\?>/g)?.length ?? 0;
  return opens > closes;
}
if (hasUnterminatedPI(xml)) throw new Error("XML has an unterminated processing instruction");

Try / catch

try {
  const doc = parseXml(xml);
} catch (err) {
  if (err instanceof Error && err.message === "Invalid XML: unterminated processing instruction") {
    // reject the document or repair the declaration before parsing
  } else throw err;
}

Prevention

When it happens

Trigger: Calling parseXml() on XML where a processing instruction or XML declaration lacks "?>" — files truncated after "<?xml version=", generated XML missing the terminator, or string manipulation that removed the tail.

Common situations: Downloads cut off mid-declaration; serialization bugs in custom XML writers; template engines that dropped a trailing "?>"; corrupted DOCX entries.

Related errors


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