Incident brief
Invalid XML document (DOCUMENT)
XMLPARSE(DOCUMENT ...) requires well-formed XML; mismatched opening and closing tags raise this data exception with a DETAIL pointing at the mismatch.
What lands in your log
ERROR: invalid XML document
In 10 seconds
- What triggers it
- Call XMLPARSE(DOCUMENT '<...>') with content whose tags do not match.
- Fix
- Correct the markup so every opening tag has a matching closing tag.
- Proof
- Reproduced on PostgreSQL 18.4 → A single SELECT reproduces SQLSTATE 2200M; the mismatched tags are rejected and the DETAIL names them.
Fix
What to do right now
Application-level steps for this error.
- Correct the markup so every opening tag has a matching closing tag.
- Validate or generate XML with a real serializer rather than string concatenation.
-- Parse only well-formed XML.
SELECT XMLPARSE(DOCUMENT '<order></order>');For this error
See this error live on the server
Run these against the affected instance to confirm the diagnosis before you act.
Invalid XML document (DOCUMENT) (2200M) is statement-local: PostgreSQL does not retain the rejected value after the statement ends. Capture the exact bound value and statement position in application/server logs, then run this targeted validation before retrying.
Validate XML document form
Check the exact XML shape before applying xmlparse/xmlcomment/xmlpi.
SELECT xml_is_well_formed_document('<root/>') AS valid_document;Why it happens
What PostgreSQL is telling you
The mechanism behind the error, grounded in the official manual, not paraphrased.
PostgreSQL 18 Documentation, Appendix A. PostgreSQL Error Codes (Table A.1, Class 22, Data Exception)
2200M → invalid_xml_documentRead the full section on postgresql.org →
Parsing a malformed XML document
The opening <order> tag is closed with </total>, so libxml reports 'Opening and ending tag mismatch' and PostgreSQL raises 'invalid XML document'.The session continues normally
The libxml parse error surfaced on a standalone statement outside a transaction, so nothing needs to be rolled back before the next query.Reproduce & verify
A real, single-session PostgreSQL reproduction
A literal transcript of SQL run against a live PostgreSQL instance in an isolated lab. The commands below are exactly what was executed.
- 1Call XMLPARSE(DOCUMENT '<...>') with content whose tags do not match.
- 2PostgreSQL (via libxml) validates that the document is well-formed.
- 3The tag mismatch aborts the statement with SQLSTATE 2200M and a DETAIL naming the tags.
A feed built XML by hand and a stray closing tag made the whole document fail to parse.
-- The error is self-contained in one statement; no schema is required.
SELECT 'no schema needed' AS setup_note;SELECT XMLPARSE(DOCUMENT '<order></total>');SELECT 'ok' AS session_after_error;What PostgreSQL actually returned
setup_note
------------------
no schema needed
(1 row)ERROR: invalid XML document
DETAIL: line 1: Opening and ending tag mismatch: order line 1 and total
<order></total>
^ session_after_error
---------------------
ok
(1 row)The same XMLPARSE succeeds once the tags match.
Without this
Before: mismatched tags abort the parse
With this, tested
After: matching tags parse cleanly
- A second operational test: exact SQL, raw output, measured result, and engineer notes
Card required. Cancel before day 7 and you are not charged.
Connected
Everything this error touches
Every page this SQLSTATE connects to: the concept that explains it, the runbooks that fix it, the parameters you tune to prevent it, and the sibling errors it travels with. All real cross-references. Jump straight in, or open the full interactive map.
Verification
- Last verified
- 2026-07-24 (isolated lab, PostgreSQL 18.4)
- Verification scope
- Verified against PostgreSQL 18.4 in an isolated lab environment
- Audit status
- reviewed
Went further?
Pro unlocks the second lab proof
Free page stops the bleeding. Pro adds the operational test, SQLSTATE audit, and deeper evidence, same error, more certainty.