XSLT - Error Handling with <xsl:try> and <xsl:catch> in XSLT 3.0

Error handling is an important feature in XSLT 3.0 because XML transformations can encounter unexpected conditions such as invalid data, division by zero, failed document access, schema-validation errors, or errors generated by functions. Instead of allowing the entire transformation to fail immediately, XSLT 3.0 provides <xsl:try> and <xsl:catch> to detect and handle dynamic errors in a controlled way. The W3C specification defines <xsl:try> as the instruction that evaluates a transformation expression or sequence constructor and <xsl:catch> as the recovery mechanism when a matching dynamic error occurs. (W3C)

1. What is <xsl:try>?

<xsl:try> defines a section of XSLT processing where a dynamic error may occur. If the processing inside this section succeeds, its result is returned normally. If a matching error occurs, XSLT searches for an appropriate <xsl:catch> and executes its recovery code.

The basic structure is:

<xsl:try>
    <!-- Instructions that may produce an error -->

    <xsl:catch>
        <!-- Error-handling instructions -->
    </xsl:catch>
</xsl:try>

The important idea is that the transformation is divided into two possible paths:

  1. Normal processing when no error occurs.

  2. Recovery processing when an error occurs.

According to the XSLT 3.0 specification, <xsl:try> can contain either a select expression or a sequence constructor, followed by one or more <xsl:catch> elements. (W3C)

2. Why is error handling needed in XSLT?

Without structured error handling, an unexpected dynamic error can cause a transformation to fail.

For example, consider this calculation:

<xsl:value-of select="100 div 0"/>

Division by zero produces a dynamic error. If this calculation is not handled, the transformation may terminate with an error.

With <xsl:try> and <xsl:catch>, you can provide an alternative result:

<xsl:try>
    <xsl:value-of select="100 div 0"/>

    <xsl:catch>
        <xsl:text>Unable to perform calculation</xsl:text>
    </xsl:catch>
</xsl:try>

Instead of allowing the error to propagate, the catch block can produce a meaningful message or perform another recovery operation.

This is particularly useful in production transformations where input data may not always be perfectly valid.

3. Understanding <xsl:catch>

<xsl:catch> contains the instructions that should execute when an error is caught.

For example:

<xsl:try>
    <xsl:value-of select="100 div 0"/>

    <xsl:catch>
        <error>
            <message>Calculation failed</message>
        </error>
    </xsl:catch>
</xsl:try>

If the calculation fails, the <xsl:catch> section becomes the recovery path.

The result produced by <xsl:catch> becomes the result of the <xsl:try> instruction. This means that the catch block does not simply report the error; it can produce replacement output. (W3C)

4. Catching a Specific Error

By default, an <xsl:catch> catches all matching dynamic errors when no errors attribute is specified.

However, you can specify particular error codes.

For example:

<xsl:try>
    <xsl:value-of select="100 div 0"/>

    <xsl:catch errors="err:FOAR0001">
        <xsl:text>Division by zero occurred.</xsl:text>
    </xsl:catch>
</xsl:try>

Here, err:FOAR0001 identifies the division-by-zero error.

The err prefix must be associated with the standard XPath/XSLT error namespace:

xmlns:err="http://www.w3.org/2005/xqt-errors"

A complete example would therefore be:

<xsl:stylesheet version="3.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:err="http://www.w3.org/2005/xqt-errors">

    <xsl:template match="/">
        <result>
            <xsl:try>
                <xsl:value-of select="100 div 0"/>

                <xsl:catch errors="err:FOAR0001">
                    <xsl:text>Division by zero occurred.</xsl:text>
                </xsl:catch>
            </xsl:try>
        </result>
    </xsl:template>

</xsl:stylesheet>

The W3C specification gives division-by-zero as a standard example of using <xsl:try> and <xsl:catch>. (W3C)

5. Catching All Errors

If you do not want to specify an individual error code, you can use:

<xsl:catch errors="*">

For example:

<xsl:try>
    <xsl:value-of select="100 div 0"/>

    <xsl:catch errors="*">
        <xsl:text>An unexpected error occurred.</xsl:text>
    </xsl:catch>
</xsl:try>

The * means that the catch clause is applicable to all error codes.

This is useful when the exact cause of the error is less important than preventing the entire transformation from failing.

However, catching every error should be used carefully. Broad error handling can hide problems that should instead be corrected in the stylesheet or source data.

6. Accessing Error Information

One of the most useful features of <xsl:catch> is that it provides information about the error.

Within <xsl:catch>, XSLT provides variables such as:

$err:code
$err:description

$err:code contains the error code, while $err:description contains a description of the error when one is available. (W3C)

For example:

<xsl:try>
    <xsl:value-of select="100 div 0"/>

    <xsl:catch errors="*">
        <error>
            <code>
                <xsl:value-of select="$err:code"/>
            </code>

            <description>
                <xsl:value-of select="$err:description"/>
            </description>
        </error>
    </xsl:catch>
</xsl:try>

This allows the generated output to contain useful diagnostic information.

7. Example with Employee Data

Consider an XML document containing employee information:

<employees>
    <employee>
        <name>John</name>
        <salary>60000</salary>
        <years>5</years>
    </employee>

    <employee>
        <name>Mary</name>
        <salary>50000</salary>
        <years>0</years>
    </employee>
</employees>

Suppose we want to calculate the employee's average salary per year:

salary div years

The second employee has zero years, which creates a division-by-zero error.

The XSLT can handle it like this:

<xsl:for-each select="employees/employee">
    <employee>
        <name>
            <xsl:value-of select="name"/>
        </name>

        <result>
            <xsl:try>
                <xsl:value-of select="salary div years"/>

                <xsl:catch errors="*">
                    <xsl:text>Calculation unavailable</xsl:text>
                </xsl:catch>
            </xsl:try>
        </result>
    </employee>
</xsl:for-each>

The first employee can be processed normally, while the invalid calculation for the second employee can be replaced with a meaningful message.

This approach is useful when processing large collections of records where individual data problems should be handled without unnecessarily disrupting the entire transformation.

8. Multiple <xsl:catch> Blocks

A single <xsl:try> can contain multiple catch clauses.

For example:

<xsl:try>
    <!-- Processing -->

    <xsl:catch errors="err:FOAR0001">
        <xsl:text>Division by zero.</xsl:text>
    </xsl:catch>

    <xsl:catch errors="err:FODC0002">
        <xsl:text>Document processing error.</xsl:text>
    </xsl:catch>

    <xsl:catch errors="*">
        <xsl:text>Unknown error.</xsl:text>
    </xsl:catch>
</xsl:try>

This provides different recovery behavior for different errors.

The order is important. If more than one catch clause matches an error, the first applicable <xsl:catch> in document order is used. (W3C)

Therefore, specific errors should generally be handled before a general errors="*" catch clause.

9. Using the select Attribute

XSLT 3.0 also allows <xsl:try> to use a select expression.

For example:

<xsl:try select="100 div 0">
    <xsl:catch errors="err:FOAR0001">
        <xsl:text>Division by zero.</xsl:text>
    </xsl:catch>
</xsl:try>

This is useful when the operation being protected is a single XPath expression.

The select form and a contained sequence constructor are alternatives. If select is present, the <xsl:try> cannot also contain ordinary processing instructions apart from <xsl:catch> and <xsl:fallback>. (W3C)

10. Using <xsl:try> with Templates and Functions

Error handling is not limited to simple expressions.

The protected section can invoke templates or functions:

<xsl:try>
    <xsl:call-template name="process-data"/>

    <xsl:catch errors="*">
        <xsl:text>Data processing failed.</xsl:text>
    </xsl:catch>
</xsl:try>

If a dynamic error occurs during processing performed by the called template, it can be caught by the surrounding <xsl:try>, provided that the error has not already been caught by a nested <xsl:try>. (W3C)

This makes <xsl:try> useful for protecting larger processing operations rather than just individual XPath expressions.

11. Nested Error Handling

XSLT also supports nested error handling.

For example:

<xsl:try>

    <xsl:try>
        <xsl:value-of select="100 div 0"/>

        <xsl:catch errors="err:FOAR0001">
            <xsl:text>Inner error handled.</xsl:text>
        </xsl:catch>
    </xsl:try>

    <xsl:catch errors="*">
        <xsl:text>Outer error handled.</xsl:text>
    </xsl:catch>

</xsl:try>

The inner <xsl:catch> gets the opportunity to handle an error first. If the inner handler successfully handles it, the outer handler does not need to process that error.

If an error is not handled by the inner try/catch structure, it can propagate to an outer <xsl:try>.

This provides a hierarchical error-handling strategy.

12. Re-throwing an Error

Sometimes a catch block should record an error but should not completely suppress it.

XSLT allows an error to be re-thrown using the error() function.

For example:

<xsl:catch errors="*">
    <xsl:message>
        Error: <xsl:value-of select="$err:description"/>
    </xsl:message>

    <xsl:sequence select="error($err:code, $err:description, $err:value)"/>
</xsl:catch>

This allows the stylesheet to perform some intermediate handling before allowing the error to propagate.

The W3C specification specifically notes that an error can be re-thrown using error($err:code, $err:description, $err:value). (W3C)

13. <xsl:try> and <xsl:catch> vs <xsl:message>

These instructions serve different purposes.

<xsl:message> is primarily used to send diagnostic messages during transformation.

For example:

<xsl:message>
    Processing employee data
</xsl:message>

By contrast, <xsl:try> and <xsl:catch> provide structured error recovery.

A message can tell you that something happened, whereas try/catch can allow the stylesheet to respond to a dynamic error.

For example:

<xsl:try>
    <xsl:value-of select="100 div 0"/>

    <xsl:catch errors="*">
        <xsl:message>
            Calculation failed.
        </xsl:message>

        <xsl:text>Default value</xsl:text>
    </xsl:catch>
</xsl:try>

Here, <xsl:message> reports the problem while <xsl:catch> determines what output should be produced.

14. Important Limitation: Dynamic Errors

A key point is that <xsl:try> is intended primarily for dynamic errors occurring during evaluation of the protected expression or sequence constructor.

It should not be viewed as a general mechanism for catching every possible stylesheet problem.

For example, errors detected while compiling or statically analyzing the stylesheet may occur before the <xsl:try> is actually executed. The W3C specification also warns that attempting to recover dynamically from some type errors is unwise because processors may detect such errors during static analysis. (W3C)

Therefore, developers should distinguish between:

Static error
    ↓
Detected while compiling/analyzing stylesheet

Dynamic error
    ↓
Occurs while transformation is executing

<xsl:try> is mainly useful for the second category.

15. Error Handling During Output Generation

Error handling becomes more complicated when the transformation has already started producing output.

XSLT 3.0 therefore defines a rollback-output attribute for <xsl:try>.

The default is:

rollback-output="yes"

This means that if an error occurs and is caught, output generated during the failed try operation may need to be effectively rolled back so that the catch result can replace it.

For example:

<xsl:try rollback-output="yes">
    <!-- Transformation that produces output -->

    <xsl:catch errors="*">
        <error>Processing failed</error>
    </xsl:catch>
</xsl:try>

The specification explains that output recovery can have performance and memory implications, particularly when processing large or streamed documents. (W3C)

16. When Should You Use <xsl:try>?

<xsl:try> and <xsl:catch> are particularly useful in situations such as:

  • Processing potentially invalid input data.

  • Performing calculations that may fail.

  • Accessing external documents.

  • Performing operations that may generate dynamic errors.

  • Handling schema-validation failures.

  • Providing meaningful fallback output.

  • Logging transformation problems.

  • Protecting individual processing operations.

  • Building robust enterprise XML transformations.

For example, if an XSLT transformation processes thousands of records from an external XML source, controlled error handling can make the transformation more resilient to unexpected data.

17. Complete Practical Example

Here is a complete XSLT 3.0 example:

<xsl:stylesheet version="3.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:err="http://www.w3.org/2005/xqt-errors">

    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="/">

        <results>

            <xsl:for-each select="employees/employee">

                <employee>

                    <name>
                        <xsl:value-of select="name"/>
                    </name>

                    <calculation>

                        <xsl:try>

                            <xsl:value-of select="salary div years"/>

                            <xsl:catch errors="err:FOAR0001">

                                <status>
                                    <xsl:text>Calculation failed: division by zero</xsl:text>
                                </status>

                            </xsl:catch>

                            <xsl:catch errors="*">

                                <status>
                                    <xsl:text>Unexpected calculation error</xsl:text>
                                </status>

                            </xsl:catch>

                        </xsl:try>

                    </calculation>

                </employee>

            </xsl:for-each>

        </results>

    </xsl:template>

</xsl:stylesheet>

The processing flow is:

Start transformation
       |
       v
Read employee
       |
       v
Calculate salary div years
       |
       +------ Success ------> Produce calculated value
       |
       +------ Division by 0 ------> First xsl:catch
       |
       +------ Other error ------> Second xsl:catch

This makes the stylesheet easier to control because different error conditions can have different recovery behavior.

18. Advantages of <xsl:try> and <xsl:catch>

Controlled failure

Instead of allowing an unexpected dynamic error to terminate processing immediately, the stylesheet can provide an alternative result.

Better diagnostics

The error code and description can be accessed through variables such as $err:code and $err:description.

Specific error handling

Different errors can be handled independently using the errors attribute.

Flexible recovery

The catch block can generate replacement XML, HTML, text, messages, or other transformation results.

Support for complex transformations

Try/catch can surround expressions, templates, functions, document processing, and other transformation operations.

Improved reliability

Applications processing external or unpredictable XML data can become more resilient to individual processing failures.

19. Important Points to Remember

  1. <xsl:try> is used to protect a section of XSLT processing.

  2. <xsl:catch> defines what happens when a matching dynamic error occurs.

  3. errors="*" catches all applicable errors.

  4. Specific error codes can be supplied through the errors attribute.

  5. $err:code provides the error code.

  6. $err:description provides an explanation of the error when available.

  7. Multiple <xsl:catch> elements can be used.

  8. The first matching catch clause is selected.

  9. Nested try/catch structures are supported.

  10. Errors can be re-thrown using the error() function.

  11. <xsl:try> is primarily intended for dynamic errors, not general stylesheet compilation errors.

  12. rollback-output controls how result-tree recovery is handled when errors occur during output generation. (W3C)

Conclusion

<xsl:try> and <xsl:catch> provide XSLT 3.0 with structured error-handling capabilities similar to exception-handling mechanisms found in conventional programming languages. They allow developers to identify dynamic errors, inspect error information, choose specific recovery strategies, and continue processing where appropriate. This is especially valuable for real-world XML transformations where source data, calculations, external documents, or validation operations cannot always be assumed to succeed.

The most important concept is to think of <xsl:try> as the protected processing area and <xsl:catch> as the recovery mechanism. By combining specific error codes, error variables, multiple catch clauses, nested handling, and controlled output recovery, XSLT 3.0 transformations can be made significantly more robust and maintainable. The behavior described above follows the W3C XSLT 3.0 specification. (W3C)