← all checks
error verified by the parser, not an XPath rule

Invalid XPath expression

Every expression a stylesheet carries must be one the processor can parse, and a stylesheet carries them in more places than its select. One is an attribute holding a bare XPath — select, test, use, value, group-by, group-adjacent, or the XSLT 3.0 key, initial-value, xpath, context-item, with-params, namespace-context, the for-each-item and for-each-source of an xsl:merge-source, and the static use-when. Another is a pattern attribute: match, count, from, group-starting-with, group-ending-with. And another is each expression a {...} encloses — in an attribute value template, in the text of an XSLT 3.0 element whose expand-text is on, or in a shadow attribute such as _select. A malformed expression breaks the transformation at runtime wherever it stands, so the sooner it surfaces the better. Only the syntax is checked, never the formatting.

A pattern is judged as a pattern, which is a narrower language than an expression rather than a second reading of one. A pattern is matched by walking up from a node, so it may only name a step such a walk can answer: child and attribute at every version, joined by self, descendant, descendant-or-self and namespace in XSLT 3.0, and never parent, ancestor, ancestor-or-self, following, following-sibling, preceding or preceding-sibling. Those seven a pattern may not name at any version, which is what makes one of them a syntax mistake rather than the version mismatch the next paragraph is about; the four XSLT 3.0 added are the language growing. A predicate inside it is an ordinary expression and may name any of them. A pattern is also a union of paths and nothing else, so 1 + 1, @a = 'b' and a, b are all perfectly good expressions and none of them is a pattern a processor will load.

The expression is read under the version in force where it stands, because the same characters are a different language under a different one. 1 cast as xs:integer is an expression in XSLT 2.0 and a syntax error in 1.0, where XPath has no cast as and reads the same text as the name cast beside the name as; map {"a": 1}, $a => f() and a intersect b are the same story at their own versions. What is reported here is text no version of the language admits, so raising the stylesheet's version will not rescue it. An expression a later version does admit is a different defect — a promise broken about which language the stylesheet is written in — and is reported as that instead.

Every prefix resolves while parsing, so an unknown prefix or a custom function is never mistaken for a syntax error. Neither is a static-type mismatch: substring-before($spans, ':') - 1 reads a numeric prefix in XPath 1.0 and is a type error in later versions, but that is what the expression means, not whether it parses. The namespace:: axis is left alone at every version, since 1.0 and 2.0 define it and a stylesheet raised to 3.0 keeps the steps it was written with. Whitespace the grammar allows is not a mistake either — a gap around the :: of an axis, or on either side of what a node test brackets, so child :: a, parent::node ( ) and element( a ) are read as the steps child::a, parent::node() and element(a) name. Only genuine syntax mistakes are reported.

Incorrect (== is not an XPath operator):

<xsl:if test="foo(a) == 'hello'">
  <xsl:value-of select="."/>
</xsl:if>

Correct:

<xsl:if test="foo(a) = 'hello'">
  <xsl:value-of select="."/>
</xsl:if>

Incorrect (a pattern cannot look sideways, so no processor loads this):

<xsl:template match="following-sibling::para">
  <xsl:value-of select="."/>
</xsl:template>

Correct (name the nodes that match, and let a predicate look where it likes):

<xsl:template match="para[preceding-sibling::*]">
  <xsl:value-of select="."/>
</xsl:template>