Posts
XSLT error messages explained: the 17 most common Saxon errors and how to fix each one
02 Jul 2026
A practical triage guide to XSLT and Saxon errors — XPST0017, XPTY0004, XPDY0002, 'Content is not allowed in prolog' and more: what each means and the fastest fix.
Quick answer: most XSLT failures fall into three buckets. If the error code starts with XP/XT + “S” (XPST, XTSE) the stylesheet itself is invalid and nothing ran. If it starts with XP/XT + “D” (XPDY, XTDE) the stylesheet compiled but hit a problem at runtime with your data. If the message mentions the XML parser (SAXParseException, SXXP0003), your input document is not well-formed XML — the stylesheet never even got a chance. Find your exact error below.
Triage: read the code before the message
| Prefix | Layer | Meaning |
|---|---|---|
XPST… | XPath, static | Expression invalid — typo, unknown function/variable/prefix |
XPTY… | XPath, type | Value has wrong type or cardinality (2.0+ strictness) |
XPDY… | XPath, dynamic | Expression valid but failed on your data/context |
XTSE… | XSLT, static | Stylesheet structure invalid |
XTDE… / XTRE… | XSLT, dynamic | Runtime failure / recoverable condition |
FO… | Function library | A standard function rejected its input |
SXXP0003 / SAXParseException | XML parser | Input is not well-formed XML |
Stylesheet won’t compile (static errors)
- XPST0017 — Cannot find a matching N-argument function — unknown function or wrong arity; very often a 2.0/3.0 function running as XSLT 1.0.
- XPST0081 — Namespace prefix has not been declared — the prefix must be declared in the stylesheet, not just the source.
- XPST0008 — Variable has not been declared — usually a scope problem: the variable died with its enclosing block.
- XTSE0630 — Variable is multiply defined — duplicate declaration, or the same module included twice.
- XTSE0010 — Element not allowed at this location — wrong nesting: output markup at top level, xsl:otherwise before xsl:when, late xsl:param.
Runs but fails on your data (dynamic & type errors)
- XPTY0004 — A sequence of more than one item is not allowed — the #1 migration error from 1.0 to 2.0+: strict typing refuses multi-node sequences and mixed-type comparisons.
- XPDY0002 — The context item is absent — relative paths inside xsl:function or with no source document.
- XTDE1490 — Cannot write more than one result document to the same URI — static href inside a loop; make it dynamic.
- XTMM9000 — Processing terminated by xsl:message — an intentional assertion fired; read the message text, not the code.
- XTRE0540 — Ambiguous rule match — two templates match with equal priority; add priority or modes.
Function library complaints
- FORX0002 — Invalid regular expression — XPath regex ≠ PCRE: no lookarounds, and double your braces inside xsl:analyze-string.
- FODC0002 — Error retrieving resource — doc()/document() URI resolution; guard with doc-available().
- FORG0001 — Invalid value for cast — dirty data meeting xs:integer()/xs:date(); guard with castable as.
Your input XML is broken (parser errors)
- SXXP0003 — Error reported by XML parser — Saxon’s wrapper; the wrapped message is the real diagnosis.
- Content is not allowed in prolog — BOM, stray bytes, or an HTML error page fed to the parser.
- The entity name must immediately follow the ‘&’ — a bare
&; write&(only 5 named entities exist in XML). - Markup following the root element must be well-formed — two root elements; wrap the fragments.
A 3-step debugging workflow
- Reproduce small. Paste stylesheet + input into XSLT Playground and cut the input down to the smallest fragment that still fails — Saxon errors carry exact line numbers, so shrinking the case pinpoints the culprit.
- Check the layer with the table above: fix stylesheet, data, or input well-formedness — they need different tools.
- Trace it. For logic errors that don’t raise codes at all (wrong output, empty output), enable the execution trace to see which templates fired and with what context. The debugging patterns guide covers this in depth.
Browse the full XSLT & Saxon Error Reference for every error above, each with before/after fixes you can run.