1. Catching Errors with Functions

Christian Grün proposed a novel approach to handling XQuery runtime errors with annotations which XQRS has decided to also support.

This involves writing error handler functions which are there to deal with particular types of runtime errors, you can use the %rest:error annotation to define the types of errors which can be handled by that function.

Consider the following example

declare
  %rest:path("/age-check/{$age}")
function age-check($age as xs:integer) {
  if($age < 18) then
    fn:error(xs:QName("xyz:NOT-OLD-ENOUGH"), "Sorry, you're not old enough.")
  else
    "All is well."
};

declare
  %rest:error("xyz:NOT-OLD-ENOUGH")
  %rest:error-param("description", "{$description}")
function not-old-enough-caught($description as xs:string) {
  $description,
  send-email('[email protected]', 'an error occurred.', $description)
};

When the Error xyz:NOT-OLD-ENOUGH is thrown by any RESTXQ function it will be caught by the not-old-enough-caught function due to the %rest:error annotation declaring that it wishes to deal with errors matching that QName xyz:NOT-OLD-ENOUGH.

The Error Handler function can give the client the HTTP Response as well as doing anything else such as sending emails etc.

In the above scenario, the description of the fn:error "Sorry, you're not old enough." was passed through to the error handler via the %rest:error-param annotation, however there are several other error parameters which can also be passed to error handler functions.

The following example is a more elaborate example of an error handler function which takes more information about what the error was and where it occurred.

declare
  %rest:error("xyz:NOT-OLD-ENOUGH")
  %rest:error-param-1("code", "{$code}")
  %rest:error-param-2("description", "{$description}")
  %rest:error-param-3("value", "{$value}")
  %rest:error-param-4("module", "{$module}")
  %rest:error-param-5("line-number", "{$line}")
  %rest:error-param-6("column-number", "{$column}")
  %rest:error-param-7("additional", "{$additional}")  
function not-old-enough-catcher(
  $code as xs:QName,
  $description as xs:string?,
  $value as item()*,
  $module as xs:string?,
  $line as xs:integer?,
  $column as xs:integer?,
  $additional as item()*) {

  do-stuff(),
  give-client-response()

};

2. Error Parameters Reference

The following table shows the list of error parameters which can be passed through to the error handler function via the use of %rest:error-param or in the case of multiple values, a numbered list of error-param annotations,
e.g. %rest:error-param-1, %rest:error-param-2, %rest:error-param-3 etc.

Variable Type Value
code xs:QName The error code
description xs:string? A description of the error condition; an empty sequence if no description is available (for example, if the error function was called with one argument).
value item()* Value associated with the error. For an error raised by calling the error function, this is the value of the third argument (if supplied).
module xs:string? The URI (or system ID) of the module containing the expression where the error occurred, or an empty sequence if the information is not available.
line-number xs:integer? The line number within the module where the error occurred, or an empty sequence if the information is not available. The value may be approximate.
column-number xs:integer? The column number within the module where the error occurred, or an empty sequence if the information is not available. The value may be approximate.
additional document-node(
  element(error:error)
)
The MarkLogic Error XML document which includes the full stack trace of the error in question.

3. Error Template Matching Syntax

The %rest:error annotation string literal can match on

  • QNames, such as err:FOAR0001
  • URI Qualified Names, such as Q{http://www.w3.org/2005/xqt-errors}FOAR0001
  • MarkLogic Error Codes, such as XDMP-DIVBYZERO

Additionally to the supported formats above, the %rest:error also supports using wildcards '*', to match part of the error, whether you want to only match on QName prefix, namespace URI, local name or in the case of using MarkLogic proprietary errors codes then either Feature ID or Error ID.

The following function would catch all standard XQuery errors

declare
  %rest:error("err:*")
  %rest:error-param("code", "{$code}")
function xqt-error($code as xs:QName) {
  "Oops ... a standard XQuery error occurred with the QName " || $code
};

While the following function would catch any MarkLogic proprietary Error with the Feature ID of "XDMP"

declare
  %rest:error("XDMP-*")
  %rest:error-param("description", "{$description}")
function marklogic-server-core-error($description as xs:string) {
  "A MarkLogic XDMP Error occurred - " || $description
};

You can use the wildcard in any position as well as using it to simply catch all types of errors in one place, whatever they may be.

declare
  %rest:error("*")
  %rest:error-param-1("code", "{$code}")
  %rest:error-param-2("description", "{$description}")
function the-mother-of-catch-alls(
  $code as xs:QName,
  $description as xs:string?) {
  "Oooops! " || $code || " - " || $description  
};

4. Error Function Preference and Specificity

Sometimes, choosing which Error Handler function to invoke for a given error can be ambiguous, in which case the more specific the %rest:error syntax the higher the preference.

The following table lists the types of syntax and the Precedence given to it.

Precedence Syntax Example
1 prefix:name
Q{uri}name
FEATURE-ERROR
err:FORG0001
Q{http://www.w3.org/2005/xqt-errors}FORG0001
XDMP-PATHRIDXNOTFOUND
2 prefix:*
Q{uri}*
FEATURE-*
err:*
Q{http://www.w3.org/2005/xqt-errors}*
XDMP-*
3 *:name
Q{*}name
*-ERROR
*:FORG0001
Q{*}FORG0001
*-PATHRIDXNOTFOUND
4 * *
TOP