1
0
mirror of https://github.com/alexandrev/xslt-lab.git synced 2026-09-16 18:23:16 +00:00

SEO phase 2: Saxon error reference (17 pages) + 2 answer-first posts

Devs search literal error messages ("XPST0017", "Content is not allowed in
prolog") with almost no competition. New /xslt/errors/ section mirrors the
function-reference pattern that already ranks:

- 17 error pages: XPST0017/0081/0008, XPTY0004, XPDY0002, XTSE0630/0010,
  XTDE1490, XTMM9000, XTRE0540, FORX0002, FODC0002, FORG0001, SXXP0003 and
  the three big XML parser messages — each with meaning, causes and
  before/after fixes
- Pillar post "XSLT error messages explained" (triage table by error-code
  prefix, links all 17)
- Post "XSLT vs DataWeave" targeting the enterprise/MuleSoft audience
- Cross-links: functions hub ↔ errors hub; FirstUpper for category headings

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MgybX3QbAWVzb9ZnroCA79
This commit is contained in:
2026-07-02 21:45:18 +00:00
parent e24ab01956
commit b666f1116c
23 changed files with 759 additions and 3 deletions
+57
View File
@@ -0,0 +1,57 @@
---
title: "XSLT error messages explained: the 17 most common Saxon errors and how to fix each one"
description: "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."
date: 2026-07-02T00:00:00Z
tags: ["xslt", "saxon", "errors", "debugging"]
---
**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](/xslt/errors/xpst0017/)** — 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](/xslt/errors/xpst0081/)** — the prefix must be declared in the *stylesheet*, not just the source.
- **[XPST0008 — Variable has not been declared](/xslt/errors/xpst0008/)** — usually a scope problem: the variable died with its enclosing block.
- **[XTSE0630 — Variable is multiply defined](/xslt/errors/xtse0630/)** — duplicate declaration, or the same module included twice.
- **[XTSE0010 — Element not allowed at this location](/xslt/errors/xtse0010/)** — 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](/xslt/errors/xpty0004/)** — 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](/xslt/errors/xpdy0002/)** — relative paths inside xsl:function or with no source document.
- **[XTDE1490 — Cannot write more than one result document to the same URI](/xslt/errors/xtde1490/)** — static href inside a loop; make it dynamic.
- **[XTMM9000 — Processing terminated by xsl:message](/xslt/errors/xtmm9000/)** — an intentional assertion fired; read the message text, not the code.
- **[XTRE0540 — Ambiguous rule match](/xslt/errors/xtre0540/)** — two templates match with equal priority; add priority or modes.
## Function library complaints
- **[FORX0002 — Invalid regular expression](/xslt/errors/forx0002/)** — XPath regex ≠ PCRE: no lookarounds, and double your braces inside xsl:analyze-string.
- **[FODC0002 — Error retrieving resource](/xslt/errors/fodc0002/)** — doc()/document() URI resolution; guard with doc-available().
- **[FORG0001 — Invalid value for cast](/xslt/errors/forg0001/)** — dirty data meeting xs:integer()/xs:date(); guard with castable as.
## Your input XML is broken (parser errors)
- **[SXXP0003 — Error reported by XML parser](/xslt/errors/sxxp0003/)** — Saxon's wrapper; the wrapped message is the real diagnosis.
- **[Content is not allowed in prolog](/xslt/errors/content-not-allowed-in-prolog/)** — BOM, stray bytes, or an HTML error page fed to the parser.
- **[The entity name must immediately follow the '&'](/xslt/errors/entity-name-must-immediately-follow/)** — a bare `&`; write `&amp;` (only 5 named entities exist in XML).
- **[Markup following the root element must be well-formed](/xslt/errors/markup-following-root-element/)** — two root elements; wrap the fragments.
## A 3-step debugging workflow
1. **Reproduce small.** Paste stylesheet + input into [XSLT Playground](https://xsltplayground.com/) 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.
2. **Check the layer** with the table above: fix stylesheet, data, or input well-formedness — they need different tools.
3. **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](/posts/xslt-debugging-patterns/) covers this in depth.
Browse the full **[XSLT & Saxon Error Reference](/xslt/errors/)** for every error above, each with before/after fixes you can run.
+80
View File
@@ -0,0 +1,80 @@
---
title: "XSLT vs DataWeave: which transformation language should integration teams use?"
description: "An honest comparison of XSLT and MuleSoft DataWeave for XML (and JSON) transformations: portability, tooling, learning curve, performance — and when each one wins."
date: 2026-07-02T00:00:00Z
tags: ["xslt", "dataweave", "mulesoft", "integration", "comparison"]
---
**Quick answer:** if your transformation lives **inside MuleSoft**, use DataWeave — it is the native language, the tooling assumes it, and fighting that assumption costs more than any XSLT advantage returns. If your transformation must be **portable across platforms** (SAP PO/CPI, TIBCO, IBM, Oracle, Java services, standalone pipelines), is **XML-centric**, or must outlive your current middleware, XSLT is the safer bet: it is a W3C standard with 25 years of guaranteed behaviour and processors on every stack.
## What each language is
**XSLT** is a W3C-standard, declarative, template-driven language (1.0 → 1999, 2.0 → 2007, 3.0 → 2017) designed for XML transformation. It runs anywhere a processor exists: Java (Saxon), .NET, C, browsers, and inside virtually every enterprise middleware — SAP, TIBCO BusinessWorks, IBM Integration Bus, Oracle SOA, Software AG.
**DataWeave** is MuleSoft's proprietary, functional, expression-based language (2.0 since Mule 4) designed for *any-to-any* transformation — JSON, XML, CSV, Java objects — with JSON as its most natural habitat.
## Head-to-head
| Dimension | XSLT | DataWeave |
|---|---|---|
| Standard | W3C, vendor-neutral | Proprietary (MuleSoft/Salesforce) |
| Runs on | Any platform with a processor | Mule runtime (plus a limited CLI) |
| Native shape | XML documents | JSON/Java structures |
| XML namespaces, mixed content | First-class | Supported but noticeably clumsier |
| JSON | Good in 3.0 (maps, xml-to-json) | Excellent, native |
| Skills market | Deep but aging pool | Growing, Mule-centric |
| Longevity risk | Very low — 25 years of stability | Tied to the MuleSoft platform |
## The same transformation, side by side
Group orders by region and total the amounts.
**XSLT 2.0:**
```xml
<xsl:template match="/orders">
<totals>
<xsl:for-each-group select="order" group-by="@region">
<region name="{current-grouping-key()}"
total="{sum(current-group()/@amount)}"/>
</xsl:for-each-group>
</totals>
</xsl:template>
```
**DataWeave 2.0:**
```
%dw 2.0
output application/xml
---
totals: {
(payload.orders.*order groupBy $.@region mapObject (orders, region) -> {
region @(name: region, total: sum(orders.@amount)): null
})
}
```
Both are compact. Notice the asymmetry, though: producing *XML attributes and nested elements* is where DataWeave needs its most awkward syntax (`@(...)`, `mapObject`), while XSLT emits them naturally — and the reverse is true for deeply JSON-shaped output.
## When XSLT wins
- **Cross-platform mandates.** The same stylesheet runs in SAP, TIBCO, IBM, a Java microservice or a batch pipeline. No rewrite when the middleware changes — this is the big one for enterprises that migrate platforms every 58 years.
- **XML-heavy domains**: SOAP services, industry standards (HL7, UBL, FpML, ISO 20022), document publishing, mixed content.
- **Complex recursive structures** — template matching handles recursion declaratively where DataWeave needs explicit recursive functions.
- **Auditability**: XSLT 1.0/2.0 behaviour is frozen by spec; transformations written in 2005 still run bit-identical today.
## When DataWeave wins
- **You are on MuleSoft.** Full stop — connectors, error handling, streaming and IDE support all assume DataWeave.
- **JSON-first APIs** with occasional XML at the edges.
- **Any-to-any mapping** (CSV → JSON → Java) in one language.
- Your team already lives in Anypoint Studio and has no XSLT background.
## Can they coexist?
Yes, and mature integration estates do exactly that: DataWeave for Mule-internal flows, XSLT for the canonical, platform-neutral transformations shared across systems. Mule can invoke XSLT via the XML module when a shared stylesheet is the source of truth.
## Try the XSLT side in 30 seconds
You don't need a platform install to evaluate XSLT: paste the grouping example above into [XSLT Playground](https://xsltplayground.com/) — a free online editor running real Saxon (XSLT 1.0/2.0/3.0), with multiple inputs, parameters and an execution trace. The [function reference](https://blog.xsltplayground.com/xslt/functions/) covers everything available in each version.
+4
View File
@@ -0,0 +1,4 @@
---
title: "XSLT & Saxon Error Reference"
description: "Common XSLT and Saxon error codes and XML parser messages explained: what each error means, typical causes, and how to fix it — with examples."
---
@@ -0,0 +1,27 @@
---
title: "Content is not allowed in prolog"
errorMessage: "org.xml.sax.SAXParseException: Content is not allowed in prolog"
description: "The XML parser found characters before the XML declaration or root element: usually a UTF-8 BOM, stray whitespace/text, or a file that is not XML at all (an HTML error page, for instance)."
date: 2026-07-02T00:00:00Z
category: "XML parsing error"
versionLabel: "All versions"
tags: ["xslt", "reference", "errors", "xml", "java"]
---
## What it means
The *prolog* is everything before the root element. Only the XML declaration, comments, processing instructions and whitespace may appear there. Any other content — even a single invisible byte — triggers this error.
## Common causes
1. **UTF-8 BOM (byte order mark).** Files saved as "UTF-8 with BOM" start with the bytes `EF BB BF`. Many Java parsers reject them when the document is served or concatenated carelessly. Save as UTF-8 *without* BOM.
2. **Stray text before `<?xml`** — a leftover log line, a shell prompt, an HTML comment from a template engine.
3. **The file is not XML at all.** Classic case: your code fetched a URL and got an HTML **error page** (login redirect, 404, proxy error) and then fed it to the XML parser. The parser dies at character 1 of `<!DOCTYPE html>`… or earlier.
4. **Encoding mismatch** — the declaration says `encoding="UTF-8"` but the bytes are UTF-16, so the first bytes are unreadable.
## How to fix it
- Inspect the first bytes with a hex viewer (`xxd file.xml | head -1`) — a BOM shows as `efbb bf`.
- If the XML comes over HTTP, log the raw response before parsing: is it actually XML?
- Re-save the file as UTF-8 without BOM and make sure nothing writes to the stream before the XML declaration.
- Paste the content into [XSLT Playground](https://xsltplayground.com/) — invisible junk becomes obvious when the parser points at line 1, column 1.
@@ -0,0 +1,26 @@
---
title: "The entity name must immediately follow the '&'"
errorMessage: "org.xml.sax.SAXParseException: The entity name must immediately follow the '&' in the entity reference."
description: "A raw ampersand appears in the XML. In XML, & always starts an entity reference — literal ampersands must be written as &amp;, and only 5 named entities exist."
date: 2026-07-02T00:00:00Z
category: "XML parsing error"
versionLabel: "All versions"
tags: ["xslt", "reference", "errors", "xml"]
---
## What it means
In XML, `&` **always** begins an entity reference like `&amp;` or `&#160;`. A bare `&` followed by a space, `=` or anything that is not a valid entity name breaks parsing immediately.
## Common causes
1. **URLs with query strings**`<link>https://x.com/?a=1&b=2</link>`. The `&b` is read as the start of an entity.
2. **Company names and free text**`<name>Johnson & Johnson</name>`.
3. **HTML named entities that XML does not define.** XML predefines only **five**: `&amp;` `&lt;` `&gt;` `&apos;` `&quot;`. Anything else (`&nbsp;`, `&eacute;`, `&copy;`…) is an error unless declared in a DTD.
## How to fix it
- Escape literal ampersands: `a=1&amp;b=2`, `Johnson &amp; Johnson`.
- Replace HTML entities with numeric character references: `&nbsp;``&#160;`, `&copy;``&#169;`, `&eacute;``&#233;`.
- For blocks full of special characters, wrap them in CDATA: `<![CDATA[a=1&b=2 <raw>]]>`.
- Remember the fix belongs in the **producer** of the XML: whatever generated the file failed to escape output. Verify the corrected input in [XSLT Playground](https://xsltplayground.com/).
+26
View File
@@ -0,0 +1,26 @@
---
title: "FODC0002"
errorMessage: "Error retrieving resource / I/O error reported by XML parser processing file:/…"
description: "doc() or document() could not load the requested document: wrong relative URI, missing file, or an environment (like an online tool) with no filesystem access."
date: 2026-07-02T00:00:00Z
category: "function error"
versionLabel: "All versions"
tags: ["xslt", "reference", "errors", "saxon"]
---
## What it means
A call to `doc()`, `document()` or `unparsed-text()` failed to retrieve the resource — the URI did not resolve, the file does not exist, or its content is not well-formed XML.
## Common causes
1. **Relative URI resolved against the wrong base.** `document('lookup.xml')` resolves relative to the **stylesheet** location (for `doc()` in some contexts, the static base URI) — not your shell's working directory.
2. **The file simply is not there** (typo, wrong folder, not deployed with the stylesheet).
3. **Sandboxed environments.** Online tools have no access to your local filesystem — `doc('file:/C:/data.xml')` cannot work in a browser-based tester.
4. **The resource exists but is not well-formed XML** — the parse failure surfaces as a retrieval error.
## How to fix it
- Guard the lookup with `doc-available()` before calling `doc()`.
- Verify the base URI with `base-uri(/)` and `static-base-uri()` — print them with `xsl:message`.
- In [XSLT Playground](https://xsltplayground.com/), don't use `doc()` for secondary inputs: pass extra documents as **named parameters** in the Input panel instead — see [working with parameters and multiple inputs](https://blog.xsltplayground.com/posts/xslt-parameters-and-multiple-inputs/).
+31
View File
@@ -0,0 +1,31 @@
---
title: "FORG0001"
errorMessage: "Invalid value for cast/constructor: xs:integer(\"abc\")"
description: "A cast or constructor function received a value it cannot convert — casting text to a number or date is the usual suspect. Guard with 'castable as' or use number()."
date: 2026-07-02T00:00:00Z
category: "function error"
versionLabel: "XSLT 2.0+"
tags: ["xslt", "reference", "errors", "saxon", "types", "xslt2"]
---
## What it means
A constructor like `xs:integer(...)`, `xs:date(...)` or an implicit cast received a lexical value that is not valid for the target type: `xs:integer('abc')`, `xs:date('2026-13-45')`, or an empty string.
## Common causes
1. **Dirty or empty data** — casting `@qty` when some elements have `qty=""`.
2. **Format mismatch**`xs:date('02/07/2026')`: XSD dates must be `2026-07-02`.
3. **Locale-style numbers**`xs:decimal('1.234,56')` fails; XSD uses `.` as decimal separator only.
## How to fix it
- Guard the cast:
```xml
<xsl:value-of select="if (@qty castable as xs:integer)
then xs:integer(@qty) else 0"/>
```
- Or use `number()` when NaN is acceptable: `number('abc')` returns `NaN` instead of raising an error — handy for optional numeric fields.
- Normalize the lexical form first (`translate()`, `replace()`, [format-date patterns](/xslt/functions/)) before casting dates and decimals.
+33
View File
@@ -0,0 +1,33 @@
---
title: "FORX0002"
errorMessage: "Invalid regular expression / Error at character N in regular expression"
description: "The pattern given to matches(), replace(), tokenize() or xsl:analyze-string is not a valid XPath regular expression — watch out for curly braces in attributes and unsupported constructs."
date: 2026-07-02T00:00:00Z
category: "function error"
versionLabel: "XSLT 2.0+"
tags: ["xslt", "reference", "errors", "saxon", "regex", "xslt2"]
---
## What it means
XPath regular expressions are based on XML Schema regex with a few additions — they are **not** PCRE/JavaScript regex. FORX0002 means the pattern itself failed to parse.
## Common causes
1. **Curly braces inside `xsl:analyze-string`'s `regex` attribute.** That attribute is an *attribute value template*, so `{` and `}` are AVT delimiters and must be doubled:
```xml
<!-- Before: FORX0002 — { interpreted as AVT -->
<xsl:analyze-string select="." regex="\d{3}">
<!-- After: double the braces -->
<xsl:analyze-string select="." regex="\d{{3}}">
```
2. **Unsupported constructs.** XPath regex has no lookahead/lookbehind (`(?=`, `(?<=`), no backreferences in `matches()` patterns' character classes, no `\b` word boundary. Rework the pattern (often with `tokenize()` + predicates).
3. **A lone `\` or trailing escape**`replace($s, '\', '/')` is invalid; escape it: `'\\'`.
4. **Invalid flags** — only `s`, `m`, `i`, `x` (and `q` in 3.0) are allowed.
## How to fix it
Test the expression interactively: paste a minimal `matches()` call in [XSLT Playground](https://xsltplayground.com/) and iterate until the pattern parses. See [replace()](/xslt/functions/xpath-replace/), [tokenize()](/xslt/functions/xpath-tokenize/) and [matches()](/xslt/functions/xpath-matches/) for the supported syntax.
@@ -0,0 +1,39 @@
---
title: "Markup in the document following the root element must be well-formed"
errorMessage: "org.xml.sax.SAXParseException: The markup in the document following the root element must be well-formed."
description: "There is content after the closing tag of the root element — usually two XML documents concatenated, or a fragment list without a wrapper element."
date: 2026-07-02T00:00:00Z
category: "XML parsing error"
versionLabel: "All versions"
tags: ["xslt", "reference", "errors", "xml"]
---
## What it means
An XML document has exactly **one** root element. Once the parser sees the root close, only comments, PIs and whitespace may follow. Any further element or text raises this error.
## Common causes
1. **Concatenated documents.** Log pipelines and batch exports often append documents into one file:
```xml
<order id="1"></order>
<order id="2"></order> <!-- error: second root -->
```
2. **A fragment list** returned by an API or built by string concatenation, with no enclosing element.
3. **Leftover text/markup** after the root when hand-editing a file.
## How to fix it
- Wrap the fragments in a single container element:
```xml
<orders>
<order id="1"></order>
<order id="2"></order>
</orders>
```
- If you cannot change the producer, wrap at read time (`concat('<wrap>', $raw, '</wrap>')` before parsing with `parse-xml()` in XSLT 3.0, or wrap in your ingestion code).
- In [XSLT Playground](https://xsltplayground.com/), paste the wrapped version and your stylesheet can iterate `wrap/order` normally.
+29
View File
@@ -0,0 +1,29 @@
---
title: "SXXP0003"
errorMessage: "Error reported by XML parser: <underlying message>"
description: "A Saxon wrapper code: the source document failed to parse as XML. The real diagnosis is in the underlying parser message that follows the code."
date: 2026-07-02T00:00:00Z
category: "XML parsing error"
versionLabel: "All versions"
tags: ["xslt", "reference", "errors", "saxon", "xml"]
---
## What it means
SXXP0003 is Saxon-specific and simply means: **your input XML is not well-formed**. The interesting part is the parser message wrapped inside — that tells you what is actually broken and at which line/column.
## The usual wrapped messages
| Wrapped parser message | Meaning |
|---|---|
| [Content is not allowed in prolog](/xslt/errors/content-not-allowed-in-prolog/) | Junk (BOM, text, whitespace) before `<?xml … ?>` or the root element |
| [The entity name must immediately follow the '&'](/xslt/errors/entity-name-must-immediately-follow/) | A bare `&` — should be `&amp;` |
| [Markup following the root element must be well-formed](/xslt/errors/markup-following-root-element/) | More than one root element |
| The element type "X" must be terminated by "</X>" | Unclosed or mis-nested tag |
| Open quote is expected for attribute | Attribute value missing quotes |
## How to fix it
1. Read the **line:column** in the message and inspect that exact spot in the input.
2. Fix the well-formedness issue in the source (it is a data problem, not a stylesheet problem).
3. Validate quickly by pasting the XML into [XSLT Playground](https://xsltplayground.com/) with an identity transform — the parser flags the precise position of the first problem.
+38
View File
@@ -0,0 +1,38 @@
---
title: "XPDY0002"
errorMessage: "The context item for axis step ./foo is absent"
description: "An expression uses a relative path (./foo, foo/bar) in a place where there is no context node — typically inside xsl:function or a stylesheet-level variable."
date: 2026-07-02T00:00:00Z
category: "XPath dynamic error"
versionLabel: "XSLT 2.0+"
tags: ["xslt", "reference", "errors", "saxon"]
---
## What it means
Relative XPath expressions need a **context item** ("where am I?"). In some places — notably inside `xsl:function` bodies — no context item exists, so any relative path fails at runtime with XPDY0002.
## Common causes
1. **Relative paths inside `xsl:function`.** Functions are called with arguments only; they have no context node.
2. **Global `xsl:variable` using a relative path** when the processor is invoked without a source document (e.g. starting at a named template with `xsl:initial-template`).
3. **Calling a named template that assumes a context** established elsewhere.
## How to fix it
Pass the nodes you need as parameters instead of relying on context:
```xml
<!-- Before: XPDY0002 — ./price has no context inside the function -->
<xsl:function name="my:total">
<xsl:sequence select="sum(./item/price)"/>
</xsl:function>
<!-- After: receive the nodes explicitly -->
<xsl:function name="my:total">
<xsl:param name="order" as="element()"/>
<xsl:sequence select="sum($order/item/price)"/>
</xsl:function>
```
If you run a stylesheet with no source document, make global variables absolute or lazy — or supply an input document. In [XSLT Playground](https://xsltplayground.com/) add the XML input in the Input panel so the context item exists.
+37
View File
@@ -0,0 +1,37 @@
---
title: "XPST0008"
errorMessage: "Variable $x has not been declared (or its declaration is not in scope)"
description: "An XPath expression references a variable that does not exist at that point: a typo, or a variable declared inside a block and used outside its scope."
date: 2026-07-02T00:00:00Z
category: "XPath static error"
versionLabel: "All versions"
tags: ["xslt", "reference", "errors", "saxon", "variables"]
---
## What it means
The expression uses `$x` but no `xsl:variable` or `xsl:param` named `x` is **visible from that point**. In XSLT, a variable's scope is its parent element, from the point of declaration onward.
## Common causes
1. **Scope confusion — the #1 cause.** A variable declared inside `xsl:if`, `xsl:when` or `xsl:for-each` disappears when that element ends:
```xml
<!-- Before: $discount is out of scope at the value-of -->
<xsl:if test="@vip = 'true'">
<xsl:variable name="discount" select="0.2"/>
</xsl:if>
<xsl:value-of select="$discount"/> <!-- XPST0008 -->
<!-- After: declare the variable once, decide the value inside it -->
<xsl:variable name="discount"
select="if (@vip = 'true') then 0.2 else 0"/>
<xsl:value-of select="$discount"/>
```
2. **Typo** in the variable name (`$totlaPrice`).
3. **Template parameters not declared.** Using `$foo` in a template that never declares `<xsl:param name="foo"/>` — declare the param even if the caller sends it with `xsl:with-param`.
## How to fix it
Move the declaration up to a scope that encloses every use, or compute conditional values *inside* the variable (as above). Remember stylesheet-level (global) variables are visible everywhere.
+40
View File
@@ -0,0 +1,40 @@
---
title: "XPST0017"
errorMessage: "Cannot find a matching 2-argument function named {namespace}name()"
description: "Saxon cannot resolve a function call: the function name is unknown, the number of arguments is wrong, or the function belongs to a newer XSLT/XPath version than the one you selected."
date: 2026-07-02T00:00:00Z
category: "XPath static error"
versionLabel: "All versions"
tags: ["xslt", "reference", "errors", "saxon"]
---
## What it means
The processor found a function call it cannot match against any known function — either the name does not exist, or no version of that function accepts the number of arguments you passed (the *arity*).
## Common causes
1. **The function belongs to a newer XSLT version.** Calling `tokenize()`, `replace()`, `matches()` or `current-group()` while running as **XSLT 1.0** fails — they were introduced in 2.0. Calling `xml-to-json()` or map/array functions under 2.0 fails — they are 3.0.
2. **Wrong number of arguments.** `substring-after('a')` raises XPST0017 because `substring-after()` needs 2 arguments.
3. **Typo in the function name.** `string-lenght()` instead of `string-length()`.
4. **Missing namespace for user/extension functions.** A function defined with `<xsl:function name="my:double">` can only be called if the prefix `my` is bound to the same namespace URI at the call site.
## How to fix it
- Check the required version in the [function reference](/xslt/functions/) and switch the processor version accordingly — in [XSLT Playground](https://xsltplayground.com/) use the version dropdown (1.0 / 2.0 / 3.0).
- Verify the arity: the message tells you how many arguments you passed (`a matching 2-argument function`).
- For user-defined functions, make sure the prefix is declared:
```xml
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="urn:my-functions">
<xsl:function name="my:double">
<xsl:param name="n"/>
<xsl:sequence select="$n * 2"/>
</xsl:function>
<xsl:template match="/">
<out><xsl:value-of select="my:double(21)"/></out>
</xsl:template>
</xsl:stylesheet>
```
+35
View File
@@ -0,0 +1,35 @@
---
title: "XPST0081"
errorMessage: "Namespace prefix 'x' has not been declared"
description: "An XPath expression uses a namespace prefix that is not declared in the stylesheet. Declaring the prefix on xsl:stylesheet fixes it."
date: 2026-07-02T00:00:00Z
category: "XPath static error"
versionLabel: "All versions"
tags: ["xslt", "reference", "errors", "saxon", "namespaces"]
---
## What it means
An XPath expression (in `select`, `match`, `test`…) uses a prefix like `soap:` or `ns0:` that the stylesheet never binds to a namespace URI. Prefixes are resolved **in the stylesheet**, not in the source document — it does not matter that the input XML declares them.
## Common causes
1. **The prefix is only declared in the source XML.** The stylesheet needs its own `xmlns:soap="…"` declaration even if the input already has one.
2. **Typo or renamed prefix**`select="soapenv:Body"` while the stylesheet declares `xmlns:soap`.
3. **Copy-pasted XPath from another stylesheet** that declared different prefixes.
## How to fix it
Declare the prefix on the root element of the stylesheet with the **same URI** used in the source document (the prefix text itself may differ; the URI is what matters):
```xml
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<xsl:template match="/soap:Envelope/soap:Body">
</xsl:template>
</xsl:stylesheet>
```
For elements in a **default namespace** (no prefix in the source), you still need a prefix in XPath 1.0/2.0 — bind any prefix to that URI and use it (`xmlns:d="urn:default-ns"``d:root/d:item`). In XSLT 3.0 you can use `xpath-default-namespace="urn:default-ns"` instead.
+34
View File
@@ -0,0 +1,34 @@
---
title: "XPTY0004"
errorMessage: "A sequence of more than one item is not allowed as the first argument of fn:string() / Cannot compare xs:string to xs:integer"
description: "A type error: an expression returned a sequence or type that does not match what the function, operator or comparison expects. The strict type rules of XPath 2.0+ are usually behind it."
date: 2026-07-02T00:00:00Z
category: "XPath type error"
versionLabel: "XSLT 2.0+"
tags: ["xslt", "reference", "errors", "saxon", "xslt2"]
---
## What it means
XPath 2.0 and 3.0 are strongly typed. XPTY0004 fires when a value's type or cardinality (how many items) does not match what the context requires — situations XSLT 1.0 silently tolerated by taking the first node or coercing values.
## Common causes
1. **A path selects multiple nodes where one is expected.** `string(//item)` fails if there are two `<item>` elements. XSLT 1.0 silently used the first one; 2.0+ refuses.
2. **Comparing different atomic types.** `@id = 42` fails when `@id` is untyped in some contexts, or `'5' > 3` — a string cannot be compared with a number.
3. **Passing a node where an atomic value is required** (or vice versa) to a typed `xsl:param`/`xsl:function`.
## How to fix it
- Select exactly one item: `string(//item[1])`, or process all of them with `for-each`/`string-join()`:
```xml
<!-- Before (XPTY0004 if several items) -->
<xsl:value-of select="string(//item)"/>
<!-- After -->
<xsl:value-of select="string-join(//item, ', ')"/>
```
- Make comparisons type-consistent: `number(@qty) > 3`, or cast explicitly with `xs:integer(@qty)`.
- When migrating 1.0 stylesheets, run them under 2.0 in [XSLT Playground](https://xsltplayground.com/) — the exact line number in the error tells you which expression needs attention.
+36
View File
@@ -0,0 +1,36 @@
---
title: "XTDE1490"
errorMessage: "Cannot write more than one result document to the same URI"
description: "Two xsl:result-document instructions resolved to the same output URI — usually a static href inside a loop. Make the href dynamic."
date: 2026-07-02T00:00:00Z
category: "XSLT dynamic error"
versionLabel: "XSLT 2.0+"
tags: ["xslt", "reference", "errors", "saxon", "xslt2"]
---
## What it means
`xsl:result-document` writes an output file per invocation. If two invocations produce the same `href`, the second would overwrite the first — the spec forbids it and Saxon raises XTDE1490.
## Common causes
1. **Static `href` inside `xsl:for-each`** — every iteration writes to the same name:
```xml
<!-- Before: every customer writes to out.xml → XTDE1490 -->
<xsl:for-each select="customer">
<xsl:result-document href="out.xml"></xsl:result-document>
</xsl:for-each>
<!-- After: unique URI per iteration -->
<xsl:for-each select="customer">
<xsl:result-document href="customer-{@id}.xml"></xsl:result-document>
</xsl:for-each>
```
2. **Duplicate keys in the data**`href="{@id}.xml"` with two equal `@id` values. Deduplicate first (`xsl:for-each-group group-by="@id"`).
3. **Writing to the principal output URI** while also producing normal output.
## How to fix it
Build the `href` from something guaranteed unique: a key from the data, `position()`, or `generate-id()`. See [xsl:result-document](/xslt/functions/xsl-result-document/) for the full syntax and a runnable example.
+30
View File
@@ -0,0 +1,30 @@
---
title: "XTMM9000"
errorMessage: "Processing terminated by xsl:message at line NN"
description: "Not a processor bug: an xsl:message with terminate=\"yes\" fired. The stylesheet stopped itself — read the message text to see why."
date: 2026-07-02T00:00:00Z
category: "XSLT dynamic error"
versionLabel: "All versions"
tags: ["xslt", "reference", "errors", "saxon", "debugging"]
---
## What it means
Someone (possibly you, possibly the author of an imported stylesheet) wrote an assertion:
```xml
<xsl:if test="not(order/@id)">
<xsl:message terminate="yes">Order without id — aborting.</xsl:message>
</xsl:if>
```
When the condition fires, the transformation stops and Saxon reports XTMM9000 with the line number of the `xsl:message`. **The error code is generic — the real information is the message text printed just before it.**
## How to fix it
1. Read the message content: it usually states the violated expectation (missing element, unexpected value, unsupported input variant).
2. Fix the **input data** if the assertion is legitimate, or
3. Fix the **assertion** if the input is actually valid and the check is too strict.
4. To keep the warning but continue processing, change to `terminate="no"` (the default) — the message still prints but the transform completes.
In [XSLT Playground](https://xsltplayground.com/) the message text appears in the error panel, so you can iterate on input or stylesheet until the assertion passes.
+28
View File
@@ -0,0 +1,28 @@
---
title: "XTRE0540"
errorMessage: "Ambiguous rule match for /root/item[1]"
description: "Two or more templates match the same node with equal priority. Saxon warns (or errors in strict mode) and picks the last one declared — make the intent explicit."
date: 2026-07-02T00:00:00Z
category: "XSLT recoverable error"
versionLabel: "All versions"
tags: ["xslt", "reference", "errors", "saxon", "templates"]
---
## What it means
During `xsl:apply-templates`, a node matched **two templates with the same import precedence and priority**. The spec calls this a *recoverable* error: processors may pick the template that appears **last** in the stylesheet and continue — Saxon does that and prints XTRE0540 as a warning by default.
## Common causes
1. **Overlapping patterns with equal default priority** — e.g. `match="item"` in two included files.
2. **Copy-pasted templates** left behind after refactoring.
3. **A generic and a specific pattern that compute the same priority** — default priorities are subtle (`item` = 0, `ns:item` = 0, `*` = 0.5, patterns with predicates = 0.5).
## How to fix it
Make the choice explicit instead of relying on declaration order:
- Add an explicit **`priority`**: `<xsl:template match="item" priority="2">`.
- Use **modes** to separate concerns: `<xsl:template match="item" mode="summary">`.
- Delete the leftover duplicate if it is dead code.
- To *find* which templates collide, run with tracing in [XSLT Playground](https://xsltplayground.com/) — the trace shows which template fired for each node. See also [template matching explained](https://blog.xsltplayground.com/posts/xslt-template-matching-explained/).
+39
View File
@@ -0,0 +1,39 @@
---
title: "XTSE0010"
errorMessage: "Element xsl:otherwise is not allowed at this location / xsl:X must not appear directly within xsl:Y"
description: "An XSLT element appears somewhere the language grammar does not allow: wrong nesting, wrong order, or content at the stylesheet top level that must live inside a template."
date: 2026-07-02T00:00:00Z
category: "XSLT static error"
versionLabel: "All versions"
tags: ["xslt", "reference", "errors", "saxon"]
---
## What it means
The stylesheet is well-formed XML, but an element sits in a position the XSLT grammar forbids. Saxon names the offending element and its location.
## Common causes
1. **Output markup at the top level.** Literal result elements (your `<html>`, `<result>`…) must be inside an `xsl:template`, never directly under `xsl:stylesheet`.
2. **Wrong order inside `xsl:choose`** — every `xsl:when` must come before `xsl:otherwise`; nothing else is allowed inside `xsl:choose`.
3. **`xsl:param` after other content.** In a template, all `xsl:param` declarations must be first.
4. **`xsl:import` not first.** `xsl:import` must precede every other declaration in the stylesheet.
5. **Instructions used as declarations** — e.g. `xsl:value-of` directly under `xsl:stylesheet`.
## How to fix it
The message is specific — it tells you which element and where. Typical corrections:
```xml
<!-- Before: literal element at top level → XTSE0010 -->
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<result/>
</xsl:stylesheet>
<!-- After: wrap it in a template -->
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<result/>
</xsl:template>
</xsl:stylesheet>
```
+25
View File
@@ -0,0 +1,25 @@
---
title: "XTSE0630"
errorMessage: "Duplicate global variable declaration / Variable 'x' is multiply defined in the same scope"
description: "Two xsl:variable or xsl:param declarations with the same name exist at the same level — often caused by a stylesheet being included twice."
date: 2026-07-02T00:00:00Z
category: "XSLT static error"
versionLabel: "All versions"
tags: ["xslt", "reference", "errors", "saxon", "variables"]
---
## What it means
The stylesheet declares the same variable or parameter name twice at the same scope level with the same import precedence. The processor cannot decide which one wins, so compilation stops.
## Common causes
1. **Literal duplicate** — two `<xsl:param name="input"/>` or `<xsl:variable name="config"/>` at the top level, often after copy-pasting a block.
2. **The same file included twice.** `xsl:include` pulls declarations in verbatim; if A includes B and C, and B also includes C, every global in C is declared twice. This is the sneaky version — each file looks correct on its own.
3. **Local duplicate in the same template** — two `xsl:variable` with the same name as siblings (re-assignment does not exist in XSLT; variables are immutable).
## How to fix it
- Remove or rename one of the duplicates. If you were trying to *update* a variable: XSLT variables cannot be reassigned — compute the final value in one declaration, or use `xsl:iterate`/recursion for running state.
- For diamond includes, switch to **`xsl:import`** (imported declarations get lower precedence, so an override is legal) or restructure so each module is included exactly once.
- The error message includes the line number of the *second* declaration — start there.
+30
View File
@@ -0,0 +1,30 @@
{{ define "title" }}XSLT &amp; Saxon Error Reference — common errors explained with fixes | {{ .Site.Title }}{{ end }}
{{ define "main" }}
<div class="page-header">
<h1>XSLT &amp; Saxon Error Reference</h1>
<p class="lead">The most common XSLT and Saxon error codes and parser messages, explained in plain English: what each error means, what typically causes it, and how to fix it — with before/after examples you can verify in the <a href="https://xsltplayground.com/">online XSLT editor</a>.</p>
</div>
<div class="content ref-intro">
<p>Saxon error codes follow the W3C convention: <strong>XPST</strong> (XPath static), <strong>XPDY</strong> (XPath dynamic), <strong>XPTY</strong> (XPath type), <strong>XTSE</strong> (XSLT static), <strong>XTDE</strong> (XSLT dynamic), <strong>FO*</strong> (function library). Parser messages such as <em>"Content is not allowed in prolog"</em> come from the underlying XML parser and mean the input is not well-formed XML. Also see the <a href="/xslt/functions/">XSLT &amp; XPath function reference</a>.</p>
</div>
{{ $cats := slice }}
{{ range .Pages }}{{ with .Params.category }}{{ $cats = $cats | append . }}{{ end }}{{ end }}
{{ $cats = uniq $cats | sort }}
{{ range $cat := $cats }}
<section class="ref-section" id="{{ $cat | urlize }}">
<h2>{{ strings.FirstUpper $cat }}s</h2>
<ul class="ref-index-list">
{{ range where $.Pages "Params.category" $cat }}
<li>
<a href="{{ .RelPermalink }}">{{ .Title }}</a>
{{ with .Params.versionLabel }}<span class="ref-badge ref-badge--sm">{{ . }}</span>{{ end }}
<span class="muted">{{ .Description | truncate 90 }}</span>
</li>
{{ end }}
</ul>
</section>
{{ end }}
{{ end }}
+32
View File
@@ -0,0 +1,32 @@
{{ define "title" }}{{ .Title }} — XSLT error: causes &amp; fixes | {{ .Site.Title }}{{ end }}
{{ define "main" }}
<article class="post ref-page">
<p class="eyebrow">XSLT Error Reference</p>
<h1>{{ .Title }}</h1>
<div class="ref-meta">
{{ with .Params.versionLabel }}<span class="ref-badge">{{ . }}</span>{{ end }}
{{ with .Params.category }}<span class="ref-badge ref-badge--cat">{{ . }}</span>{{ end }}
</div>
{{ with .Params.errorMessage }}
<div class="ref-syntax">
<span class="ref-syntax-label">Error message</span>
<pre><code>{{ . }}</code></pre>
</div>
{{ end }}
{{ with .Description }}<p class="lead">{{ . }}</p>{{ end }}
{{ partial "adsense/in-article.html" . }}
<div class="content">
{{ .Content }}
</div>
{{- if and .Site.Params.ethicalads.publisher (ne hugo.Environment "development") -}}
<div class="ad">
<div class="ethical-ad" data-ea-publisher="{{ .Site.Params.ethicalads.publisher }}" data-ea-type="text"></div>
</div>
{{- end }}
<div class="ref-try">
<a href="https://xsltplayground.com/" class="button" target="_blank" rel="noopener noreferrer">Reproduce &amp; fix it in XSLT Playground →</a>
</div>
<p class="muted back-link"><a href="{{ .CurrentSection.RelPermalink }}">← Back to XSLT Error Reference</a></p>
{{ partial "related-posts.html" . }}
</article>
{{ end }}
+3 -3
View File
@@ -15,12 +15,12 @@
<strong>Jump to:</strong>
<a href="#xslt-elements">XSLT Elements ({{ len $elements }})</a>
{{ range $cats }}
<a href="#{{ . | urlize }}">{{ humanize . }}s ({{ len (where $functions "Params.category" .) }})</a>
<a href="#{{ . | urlize }}">{{ strings.FirstUpper . }}s ({{ len (where $functions "Params.category" .) }})</a>
{{ end }}
</nav>
<div class="content ref-intro">
<p>Looking for how a specific XSLT instruction or XPath function works? This reference covers the <strong>string, numeric, boolean, date, node, sequence, map, array, JSON and higher-order functions</strong> available in each XSLT version, plus every <strong>XSLT element</strong> from <code>xsl:template</code> to <code>xsl:accumulator</code>. Entries marked <em>XSLT 2.0</em> or <em>XSLT 3.0</em> require a processor such as Saxon — exactly what <a href="https://xsltplayground.com/">XSLT Playground</a> runs, so you can verify behaviour before shipping.</p>
<p>Looking for how a specific XSLT instruction or XPath function works? This reference covers the <strong>string, numeric, boolean, date, node, sequence, map, array, JSON and higher-order functions</strong> available in each XSLT version, plus every <strong>XSLT element</strong> from <code>xsl:template</code> to <code>xsl:accumulator</code>. Entries marked <em>XSLT 2.0</em> or <em>XSLT 3.0</em> require a processor such as Saxon — exactly what <a href="https://xsltplayground.com/">XSLT Playground</a> runs, so you can verify behaviour before shipping. Hitting an error code instead? See the <a href="/xslt/errors/">XSLT &amp; Saxon error reference</a>.</p>
</div>
{{ if $elements }}
@@ -40,7 +40,7 @@
{{ range $cat := $cats }}
<section class="ref-section" id="{{ $cat | urlize }}">
<h2>{{ humanize $cat }}s</h2>
<h2>{{ strings.FirstUpper $cat }}s</h2>
<ul class="ref-index-list">
{{ range where $functions "Params.category" $cat }}
<li>