# 2200L — not_an_xml_document

> Source-backed reference for PostgreSQL SQLSTATE 2200L.
---

# 2200L

## At a glance {#at-a-glance}
The SQL/XML `SQLSERIALIZE` `DOCUMENT` operation received input that could not satisfy the requested document form. PostgreSQL 18.6 maps this public DOCUMENT path through `xmltotext_with_options` with `XMLOPTION_DOCUMENT`; a failed or soft parse is mapped to `not an XML document`. The wrapper’s early non-DOCUMENT/no-indent bypass is not a CONTENT-mode 2200L claim, and this entry does not cover every XML content or comment violation.

<!-- BEGIN SQLSTATE FACTS: generated by scripts/generate.py; do not edit -->

| Field | Value |
| --- | --- |
| SQLSTATE | `2200L` |
| Condition | `not_an_xml_document` |
| Status | `active` |
| Known present by | `8.3.0` |
| Locked snapshots | `9.0.23, 9.1.24, 9.2.24, 9.3.25, 9.4.26, 9.5.25, 9.6.24, 10.23, 11.22, 12.22, 13.23, 14.24, 15.19, 16.15, 17.11, 18.6, 19beta3` |
| Macros | `ERRCODE_NOT_AN_XML_DOCUMENT` |
| Aliases | `—` |

<!-- source facts: data/errcodes/2200L.json -->
<!-- END SQLSTATE FACTS -->

## Meaning {#meaning}
This member is the document-level XML failure for the public SQL/XML `SQLSERIALIZE` `DOCUMENT` form. That operation reaches `xmltotext_with_options` with `XMLOPTION_DOCUMENT`; it calls `xml_parse` with an `ErrorSaveContext`, and when no document is returned or the saved parse error is set, the wrapper frees the partial document and raises 2200L. Its early guard returns binary-compatible input for non-DOCUMENT/no-indent, so this mapping should not be generalized to `CONTENT`. The lower `xml_parse` routine has separate 2200M/2200N branches, so this wrapper must not be read as a synonym for every parser error.

## Messages {#messages}

The SQLSERIALIZE `DOCUMENT` wrapper raises `ERROR` with primary `not an XML document`. The cited branch has no separate DETAIL or HINT; any parser-specific context must be taken from the actual runtime error and build.

## Diagnosis {#diagnosis}
Capture the full context and the expression supplied to SQLSERIALIZE. Check whether the operation requested `DOCUMENT` or `CONTENT`, then inspect the document boundary, top-level structure, and encoding. A 2200L diagnosis is tied to the `DOCUMENT` mapping described above; do not infer it from a `CONTENT` request or conflate member codes 2200M/N/S/T with this document-level condition.

## Response {#response}
Repair the XML document boundary and structure identified by the parser, preserving the caller’s required document-versus-fragment requirement.

When this branch raises `ERROR`, an explicit transaction must first be recovered with `ROLLBACK` or `ROLLBACK TO SAVEPOINT` for a savepoint established before the statement; in autocommit, retry only the corrected action after the failed statement completes. See the [transaction and retry guide](../guides/) for this boundary rule.

## Versions {#versions}
The locked catalogue records this condition from 8.3.0; fixed source coverage is PostgreSQL 18.6.

## Related {#related}
[`2200M`](../2200m/), [`2200N`](../2200n/), [`2200S`](../2200s/), [`2200T`](../2200t/)

## Sources {#sources}
Fixed source: [`src/backend/utils/adt/xml.c#L669-697`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/adt/xml.c#L669-L697). The structured [evidence record](../data/evidence/2200l.json) retains the document-option guard, message, and scope boundaries. The source confirms the wrapper mapping; the precise input and any libxml2 context are runtime-dependent. No natural XML case was run for this page.
