Skip to main content

Error Processing

Managing the behavior of Caché when an error occurs is called error processing or error handling. Error processing performs one or more of the following functions:

  • Correcting the condition that caused the error

  • Performing some action that allows execution to resume despite the error

  • Diverting the flow of execution

  • Logging information about the error

Caché supports three types of error processing, which can be used simultaneously. These are:

Important:

The preferred mechanism for ObjectScript error handling is the TRY-CATCH mechanism. If traditional error processing is to be used, the $ZTRAP mechanism is preferred. Use of $ETRAP is discouraged.

The TRY-CATCH Mechanism

Caché supports a TRY-CATCH mechanism for handling errors. With this mechanism, you can establish delimited blocks of code, each called a TRY block; if an error occurs during a TRY block, control passes to the TRY block’s associated CATCH block, which contains code for handling the exception. A TRY block can also include THROW commands; each of these commands explicitly issues an exception from within a TRY block and transfers execution to a CATCH block.

To use this mechanism in its most basic form, include a TRY block within ObjectScript code. If an exception occurs within this block, the code within the associated CATCH block is then executed. The form of a TRY-CATCH block is:

 TRY {
      protected statements
 } CATCH [ErrorHandle] {
      error statements
 }
 further statements

where:

  • The TRY command identifies a block of ObjectScript code statements enclosed in curly braces. TRY takes no arguments. This block of code is protected code for structured exception handling. If an exception occurs within a TRY block, Caché sets the exception properties (oref.Name, oref.Code, oref.Data, and oref.Location), $ZERROR, and $ECODE, then transfers execution to an exception handler, identified by the CATCH command. This is known as throwing an exception.

  • The protected statements are ObjectScript statements that are part of normal execution. (These can include calls to the THROW command. This scenario is described in the following section.)

  • The CATCH command defines an exception handler, which is a block of code to execute when an exception occurs in a TRY block.

  • The ErrorHandle variable is a handle to an exception object. This can be either an exception object that Caché has generated in response to a runtime error or an exception object explicitly issued by invoking the THROW command (described in the next section).

  • The error statements are ObjectScript statements that are invoked if there is an exception.

  • The further statements are ObjectScript statements that either follow execution of the protected statements if there is no exception or follow execution of the error statements if there is an exception and control passes out of the CATCH block.

Depending on events during execution of the protected statements, one of the following events occurs:

  • If an error does not occur, execution continues with the further statements that appear outside the CATCH block.

  • If an error does occur, control passes into the CATCH block and error statements are executed. Execution then depends on contents of the CATCH block:

    • If the CATCH block contains a THROW or GOTO command, control goes directly to the specified location.

    • If the CATCH block does not contain a THROW or GOTO command, control passes out of the CATCH block and execution continues with the further statements.

Using THROW with TRY-CATCH

Caché issues an implicit exception when a runtime error occurs. To issue an explicit exception, the THROW command is available. The THROW command transfers execution from the TRY block to the CATCH exception handler. The THROW command has a syntax of:

THROW expression

where expression is an instance of a class that inherits from the %Exception.AbstractExceptionOpens in a new tab class, which Caché provides for exception handling. For more information on %Exception.AbstractExceptionOpens in a new tab, see the following section.

The form of the TRY/CATCH block with a THROW is:

 TRY {
      protected statements
      THROW expression
      protected statements
 }
 CATCH exception {
      error statements
 }
 further statements

where the THROW command explicitly issues an exception. The other elements of the TRY-CATCH block are as described in the previous section.

The effects of THROW depends on where the throw occurs and the argument of THROW:

  • A THROW within a TRY block passes control to the CATCH block.

  • A THROW within a CATCH block passes control up the execution stack to the next error handler. If the exception is a %Exception.SystemException object, the next error handler can be any type (CATCH or traditional); otherwise there must be a CATCH to handle the exception or a <NOCATCH> error will be thrown.

If control passes into a CATCH block because of a THROW with an argument, the ErrorHandle contains the value from the argument. If control passes into a CATCH block because of a system error, the ErrorHandle is a %Exception.SystemExceptionOpens in a new tab object. If no ErrorHandle is specified, there is no indication of why control has passed into the CATCH block.

For example, suppose there is code to divide two numbers:

div(num,div) public {
 TRY {
  SET ans=num/div
 } CATCH errobj {
  IF errobj.Name="<DIVIDE>" { SET ans=0 }
  ELSE { THROW errobj }
 }
 QUIT ans
}

If a divide-by-zero error happens, the code is specifically designed to return zero as the result. For any other error, the THROW sends the error on up the stack to the next error handler.

Using $$$ThrowOnError and $$$ThrowStatus Macros

Caché provides macros for use with exception handling. When invoked, these macros throw an exception object to the CATCH block.

The following example invokes the $$$ThrowOnError() macro when an error status is returned by the %Prepare() method:

  #include %occStatus
  ZNSPACE "SAMPLES"
  TRY {
    SET myquery = "SELECT TOP 5 Name,Hipness,DOB FROM Sample.Person"
    SET tStatement = ##class(%SQL.Statement).%New()
    SET status = tStatement.%Prepare(myquery)
    $$$ThrowOnError(status)
    WRITE "%Prepare succeeded",!
    RETURN
  }
  CATCH sc {
    WRITE "In Catch block",!
    WRITE "error code: ",sc.Code,!
    WRITE "error location: ",sc.Location,!
    WRITE "error data:",$LISTGET(sc.Data,2),!
  RETURN
  }

The following example invokes $$$ThrowStatus after testing the value of the error status returned by the %Prepare() method:

  #include %occStatus
  ZNSPACE "SAMPLES"
  TRY {
    SET myquery = "SELECT TOP 5 Name,Hipness,DOB FROM Sample.Person"
    SET tStatement = ##class(%SQL.Statement).%New()
    SET status = tStatement.%Prepare(myquery)
    IF ($System.Status.IsError(status)) {
      WRITE "%Prepare failed",!
      $$$ThrowStatus(status) }
    ELSE {WRITE "%Prepare succeeded",!
      RETURN }
  }
  CATCH sc {
    WRITE "In Catch block",!
    WRITE "error code: ",sc.Code,!
    WRITE "error location: ",sc.Location,!
    WRITE "error data:",$LISTGET(sc.Data,2),!
  RETURN
  }

These system-supplied macros are further described in the “ObjectScript Macros and the Macro Preprocessor” chapter of this book.

Using the %Exception.SystemException and %Exception.AbstractException Classes

Caché provides the %Exception.SystemExceptionOpens in a new tab and %Exception.AbstractExceptionOpens in a new tab classes for use with exception handling. %Exception.SystemExceptionOpens in a new tab inherits from the %Exception.AbstractExceptionOpens in a new tab class and is used for system errors. For custom errors, create a class that inherits from %Exception.AbstractExceptionOpens in a new tab. %Exception.AbstractExceptionOpens in a new tab contains properties such as the name of the error and the location at which it occurred.

When a system error is caught within a TRY block, the system creates a new instance of the %Exception.SystemExceptionOpens in a new tab class and places error information in that instance. When throwing a custom exception, the application programmer is responsible for populating the object with error information.

An exception object has the following properties:

  • Name — The error name, such as <UNDEFINED>

  • Code — The error number

  • Location — The label+offset^routine location of the error

  • Data — Any extra data reported by the error, such as the name of the item causing the error

Other Considerations with TRY-CATCH

The following describe conditions that may arise when using a TRY-CATCH block.

QUIT within a TRY-CATCH Block

A QUIT command within a TRY or CATCH block passes control out of the block to the next statement after the TRY-CATCH as a whole.

TRY-CATCH and the Execution Stack

The TRY block does not introduce a new level in the execution stack. This means that it is not a scope boundary for NEW commands. The error statements execute at the same level as that of the error. This can result in unexpected results if there are DO commands within the protected statements and the DO target is also within the protected statements. In such cases, the $ESTACK special variable can provide information about the relative execution levels.

Using TRY-CATCH with Traditional Error Processing

TRY-CATCH error processing is compatible with $ZTRAP and $ETRAP error traps used at different levels in the execution stack. The exception is that $ZTRAP and $ETRAP may not be used within the protected statements of a TRY clause. User-defined errors with a THROW are limited to TRY-CATCH only. User-defined errors with the ZTRAP command may be used with any type of error processing.

%Status Error Processing

Many of the methods in the Caché class library return success or failure information via the %StatusOpens in a new tab data type. For example, the %Save() method, used to save an instance of a %PersistentOpens in a new tab object, returns a %StatusOpens in a new tab value indicating whether or not the object was saved.

  • Successful method execution returns a %StatusOpens in a new tab of 1.

  • Failed method execution returns %StatusOpens in a new tab as an encoded string containing the error status and one or more error codes and text messages. Status text messages are localized for the language of your locale.

You can use %SYSTEM.StatusOpens in a new tab class methods to inspect and manipulate %StatusOpens in a new tab values.

Caché provides several options for displaying (writing) the %StatusOpens in a new tab encoded string in different formats. For further details, refer to “Display (Write) Commands” in the “Commands” chapter of this manual.

In the following example, the %Prepare fails because of an error in the myquery text: “ZOP” should be “TOP”. This error is detected by the IsError()Opens in a new tab method, and other %SYSTEM.StatusOpens in a new tab methods display the error code and text:

  ZNSPACE "SAMPLES"
  SET myquery = "SELECT ZOP 5 Name,DOB FROM Sample.Person"
  SET tStatement = ##class(%SQL.Statement).%New()
  SET status = tStatement.%Prepare(myquery)
  IF ($System.Status.IsError(status)) {
      WRITE "%Prepare failed",!
      DO StatusError() }
  ELSE {WRITE "%Prepare succeeded",!
        RETURN }
StatusError()
  WRITE "Error #",$System.Status.GetErrorCodes(status),!
  WRITE $System.Status.GetOneStatusText(status,1),!
  WRITE "end of error display"
  QUIT

The following example is the same as the previous, except that the status error is detected by the $$$ISERR() macro of the %occStatus include file. $$$ISERR() (and its inverse, $$$ISOK()) checks whether or not %StatusOpens in a new tab=1. The error code is returned by the $$$GETERRORCODE() macro:

#include %occStatus
  ZNSPACE "SAMPLES"
  SET myquery = "SELECT ZOP 5 Name,DOB FROM Sample.Person"
  SET tStatement = ##class(%SQL.Statement).%New()
  SET status = tStatement.%Prepare(myquery)
  IF $$$ISERR(status) {
      WRITE "%Prepare failed",!
      DO StatusError() }
  ELSE {WRITE "%Prepare succeeded",!
        RETURN}
StatusError()
  WRITE "Error #",$$$GETERRORCODE(status),!
  WRITE $System.Status.GetOneStatusText(status,1),!
  WRITE "end of error display"
  QUIT

These system-supplied macros are further described in the “ObjectScript Macros and the Macro Preprocessor” chapter of this book.

Some methods, such as %New(), generate, but do not return a %StatusOpens in a new tab. %New() either returns an oref to an instance of the class upon success, or the null string upon failure. You can retrieve the status value for methods of this type by accessing the %objlasterror system variable, as shown in the following example.

  SET session = ##class(%CSP.Session).%New()
  IF session="" {
      WRITE "session oref not created",!
      WRITE "%New error is ",!,$System.Status.GetErrorText(%objlasterror),! }
  ELSE {WRITE "session oref is ",session,! }

For more information, refer to the %SYSTEM.StatusOpens in a new tab class.

Creating %Status Errors

You can invoke system-defined %Status errors from your own methods by using the Error()Opens in a new tab method. You specify the error number that corresponds to the error message you wish to return.

  WRITE "Here my method generates an error",!
  SET status = $System.Status.Error(20)
  WRITE $System.Status.GetErrorText(status),!

You can include %1, %2, and %3 parameters in the returned error message, as shown in the following example:

  WRITE "Here my method generates an error",!
  SET status = $System.Status.Error(214,"3","^fred","BedrockCode")
  WRITE $System.Status.GetErrorText(status),!

You can localize the error message to display in your preferred language.

  SET status = $System.Status.Error(30)
  WRITE "In English:",!
  WRITE $System.Status.GetOneStatusText(status,1,"en"),!
  WRITE "In French:",!
  WRITE $System.Status.GetOneStatusText(status,1,"fr"),!

For a list of error codes and messages (in English), refer to the “General Error Messages” chapter of the Caché Error Reference.

You can use the generic error codes 83 and 5001 to specify a custom message that does not correspond to any of the general error messages.

You can use the AppendStatus()Opens in a new tab method to create a list of multiple error messages. Then you can use GetOneErrorText()Opens in a new tab or GetOneStatusText()Opens in a new tab to retrieve individual error messages by their position in this list:

CreateCustomErrors
  SET st1 = $System.Status.Error(83,"my unique error")
  SET st2 = $System.Status.Error(5001,"my unique error")
  SET allstatus = $System.Status.AppendStatus(st1,st2)
DisplayErrors
  WRITE "All together:",!
  WRITE $System.Status.GetErrorText(allstatus),!!
  WRITE "One by one",!
  WRITE "First error format:",!
  WRITE $System.Status.GetOneStatusText(allstatus,1),!
  WRITE "Second error format:",!
  WRITE $System.Status.GetOneStatusText(allstatus,2),!

%SYSTEM.Error

The %SYSTEM.ErrorOpens in a new tab class is a generic error object. It can be created from a %Status error, from an exception object, a $ZERROR error, or an SQLCODE error. You can use %SYSTEM.ErrorOpens in a new tab class methods to convert a %Status to an exception, or to convert an exception to a %Status.

Traditional Error Processing

This section describes various aspects of traditional error processing with Caché. These include:

How Traditional Error Processing Works

For traditional error processing, Caché provides the functionality so that your application can have an error handler. An error handler processes any error that may occur while the application is running. A special variable specifies the ObjectScript commands to be executed when an error occurs. These commands may handle the error directly or may call a routine to handle it.

To set up an error handler, the basic process is:

  1. Create one or more routines to perform error processing. Write code to perform error processing. This can be general code for the entire application or specific processing for specific error conditions. This allows you to perform customized error handling for each particular part of an application.

  2. Establish one or more error handlers within your application, each using specific appropriate error processing.

If an error occurs and no error handler has been established, the behavior depends on how the Caché session was started:

  1. If you signed onto Caché at the Terminal prompt and have not set an error trap, Caché displays an error message on the principal device and returns the Terminal prompt with the program stack intact. The programmer can later resume execution of the program.

  2. If you invoked Caché in Application Mode and have not set an error trap, Caché displays an error message on the principal device and executes a HALT command.

Internal Error-Trapping Behavior

To get the full benefit of Caché error processing and the scoping issues surrounding the $ZTRAP special variable (as well as $ETRAP), it is helpful to understand how Caché transfers control from one routine to another.

Caché builds a data structure called a “context frame” each time any of the following occurs:

  • A routine calls another routine with a DO command. (This kind of frame is also known as a “DO frame.”)

  • An XECUTE command argument causes ObjectScript code to execute. (This kind of frame is also known as a “XECUTE frame.”)

  • A user-defined function is executed.

The frame is built on the call stack, one of the private data structures in the address space of your process. Caché stores the following elements in the frame for a routine:

  • The value of the $ZTRAP special variable (if any)

  • The value of the $ETRAP special variable (if any)

  • The position to return from the subroutine

When routine A calls routine B with DO ^B, Caché builds a DO frame on the call stack to preserve the context of A. When routine B calls routine C, Caché adds a DO frame to the call stack to preserve the context of B, and so forth.

Frames on a Call Stack
generated description: errors framestack

If routine A in the figure above is invoked at the Terminal prompt using the DO command, then an extra DO frame, not described in the figure, exists at the base of the call stack.

Current Context Level

You can use the following to return information about the current context level:

  • The $STACK special variable contains the current relative stack level.

  • The $ESTACK special variable contains the current stack level. It can be initialized to 0 (level zero) at any user-specified point.

  • The $STACK function returns information about the current context and contexts that have been saved on the call stack

The $STACK Special Variable

The $STACK special variable contains the number of frames currently saved on the call stack for your process. The $STACK value is essentially the context level number (zero based) of the currently executing context. Therefore, when a Caché image is started, but before any commands are processed, the value of $STACK is 0.

See the $STACK special variable in the Caché ObjectScript Reference for details.

The $ESTACK Special Variable

The $ESTACK special variable is similar to the $STACK special variable, but is more useful in error handling because you can reset it to 0 (and save its previous value) with the NEW command. Thus, a process can reset $ESTACK in a particular context to mark it as a $ESTACK level 0 context. Later, if an error occurs, error handlers can test the value of $ESTACK to unwind the call stack back to that context.

See the $ESTACK special variable in the Caché ObjectScript Reference for details.

The $STACK Function

The $STACK function returns information about the current context and contexts that have been saved on the call stack. For each context, the $STACK function provides the following information:

  • The type of context (DO, XECUTE, or user-defined function)

  • The entry reference and command number of the last command processed in the context

  • The source routine line or XECUTE string that contains the last command processed in the context

  • The $ECODE value of any error that occurred in the context (available only during error processing when $ECODE is non-null)

When an error occurs, all context information is immediately saved on your process error stack. The context information is then accessible by the $STACK function until the value of $ECODE is cleared by an error handler. In other words, while the value of $ECODE is non-null, the $STACK function returns information about a context saved on the error stack rather than an active context at the same specified context level.

See the $STACK function in the Caché ObjectScript Reference for details.

When an error occurs and an error stack already exists, Caché records information about the new error at the context level where the error occurred, unless information about another error already exists at that context level on the error stack. In this case, the information is placed at the next level on the error stack (regardless of the information that may already be recorded there).

Therefore, depending on the context level of the new error, the error stack may extend (one or more context levels added) or information at an existing error stack context level may be overwritten to accommodate information about the new error.

Keep in mind that you clear your process error stack by clearing the $ECODE special variable.

Error Codes

When an error occurs, Caché sets the $ZERROR and $ECODE special variables to a value describing the error.

$ZERROR Value

Caché sets $ZERROR to a string containing:

  • The Caché error code, enclosed in angle brackets.

  • The label, offset, and routine name where the error occurred.

  • (For some errors): Additional information, such as the name of the item that caused the error.

The AsSystemError()Opens in a new tab method of the %Exception.SystemExceptionOpens in a new tab class returns the same values in the same format as $ZERROR.

The following examples show the type of messages to which $ZERROR is set when Caché encounters an error. In the following example, the undefined local variable abc is invoked at line offset 2 from label PrintResult of routine MyTest. $ZERROR contains:

<UNDEFINED>PrintResult+2^MyTest *abc

The following error occurred when a non-existent class is invoked at line offset 3:

<CLASS DOES NOT EXIST>PrintResult+3^MyTest *%SYSTEM.XXQL

The following error occurred when a non-existent method of an existing class is invoked at line offset 4:

<METHOD DOES NOT EXIST>PrintResult+4^MyTest *BadMethod,%SYSTEM.SQL

You can also explicitly set the special variable $ZERROR as any string up to 128 characters; for example:

 SET $ZERROR="Any String"

The $ZERROR value is intended for use immediately following an error. Because a $ZERROR value may not be preserved across routine calls, users that wish to preserve a $ZERROR value for later use should copy it to a variable. It is strongly recommended that users set $ZERROR to the null string ("") immediately after use. See the $ZERROR special variable in the Caché ObjectScript Reference for details. For further information on handling $ZERROR errors, refer to the %SYSTEM.ErrorOpens in a new tab class methods in the InterSystems Class Reference.

$ECODE Value

When an error occurs, Caché sets $ECODE to the value of a comma-surrounded string containing the ANSI Standard error code that corresponds to the error. For example, when you make a reference to an undefined global variable, Caché sets $ECODE set to the following string:

,M7,

If the error has no corresponding ANSI Standard error code, Caché sets $ECODE to the value of a comma-surrounded string containing the Caché error code preceded by the letter Z. For example, if a process has exhausted its symbol table space, Caché places the error code <STORE> in the $ZERROR special variable and sets $ECODE to this string:

,ZSTORE,

After an error occurs, your error handlers can test for specific error codes by examining the value of the $ZERROR special variable or the $ECODE special variable.

Note:

Error handlers should examine $ZERROR rather than $ECODE special variable for specific errors.

See the $ECODE special variable in the Caché ObjectScript Reference for details.

Handling Errors with $ZTRAP

To handle errors with $ZTRAP, you set the $ZTRAP special variable to a location, specified as a quoted string. You set the $ZTRAP special variable to an entry reference that specifies the location to which control is to be transferred when an error occurs. You then write $ZTRAP code at that location.

When you set $ZTRAP to a non-empty value, it takes precedence over any existing $ETRAP error handler. Caché implicitly performs a NEW $ETRAP command and sets $ETRAP equal to "".

Setting $ZTRAP in a Procedure

Within a procedure, you can only set the $ZTRAP special variable to a line label (private label) within that procedure. You cannot set $ZTRAP to any external routine from within a procedure block.

When displaying the $ZTRAP value, Caché does not return the name of the private label. Instead, it returns the offset from the top of the procedure where that private label is located.

For further details see the $ZTRAP special variable in the ObjectScript Reference.

Setting $ZTRAP in a Routine

Within a routine, you can set the $ZTRAP special variable to a label in the current routine, to an external routine, or to a label within an external routine. You can only reference an external routine if the routine is not procedure block code. The following example establishes LogErr^ErrRou as the error handler. When an error occurs, Caché executes the code found at the LogErr label within the ^ErrRou routine:

  SET $ZTRAP="LogErr^ErrRou"

When displaying the $ZTRAP value, Caché displays the label name and (when appropriate) the routine name.

A label name must be unique within its first 31 characters. Label names and routine names are case-sensitive.

Within a routine, $ZTRAP has three forms:

  • SET $ZTRAP="location"

  • SET $ZTRAP="*location" which executes in the context in which the error occurred that invoked it.

  • SET $ZTRAP="^%ETN" which executes the system-supplied error routine %ETN in the context in which the error occurred that invoked it. You cannot execute ^%ETN (or any external routine) from a procedure block. Either specify the code is [Not ProcedureBlock], or use a routine such as the following, which invokes the %ETN entrypoint BACK^%ETN:

    ClassMethod MyTest() as %Status
      {
      SET $ZTRAP="Error"
      SET ans = 5/0      /* divide-by-zero error */
      WRITE "Exiting ##class(User.A).MyTest()",!
      QUIT ans
    Error
      SET err=$ZERROR
      SET $ZTRAP=""
      DO BACK^%ETN
      QUIT $$$ERROR($$$CacheError,err)
      } 
    

    For more information on %ETN and its entrypoints, see Logging Application Errors. For details on its use with $ZTRAP, see SET $ZTRAP=^%ETN.

For further details see the $ZTRAP special variable in the ObjectScript Reference.

Writing $ZTRAP Code

The location that $ZTRAP points to can perform a variety of operations to display, log, and/or correct an error. Regardless of what error handling operations you wish to perform, the $ZTRAP code should begin by performing two tasks:

  • Set $ZTRAP to another value, either the location of an error handler, or the empty string (""). (You must use SET, because you cannot KILL $ZTRAP.) This is done because if another error occurs during error handling, that error would invoke the current $ZTRAP error handler. If the current error handler is the error handler you are in, this would result in an infinite loop.

  • Set a variable to $ZERROR. If you wish to reference a $ZERROR value later in your code, refer to this variable, not $ZERROR itself. This is done because $ZERROR contains the most-recent error, and a $ZERROR value may not be preserved across routine calls, including internal routine calls. If another error occurs during error handling, the $ZERROR value would be overwritten by that new error.

    It is strongly recommended that users set $ZERROR to the null string ("") immediately after use.

The following example shows these essential $ZTRAP code statements:

MyErrHandler
  SET $ZTRAP=""
  SET err=$ZERROR
  /* error handling code
     using err as the error
     to be handled */

Using $ZTRAP

Each routine in an application can establish its own $ZTRAP error handler by setting $ZTRAP. When an error trap occurs, Caché takes the following steps:

  1. Sets the special variable $ZERROR to an error message.

  2. Resets the program stack to the state it was in when the error trap was set (when the SET $ZTRAP= was executed). In other words, the system removes all entries on the stack until it reaches the point at which the error trap was set. (The program stack is not reset if $ZTRAP was set to a string beginning with an asterisk (*).)

  3. Resumes the program at the location specified by the value of $ZTRAP. The value of $ZTRAP remains the same.

    Note:

    You can explicitly set the variable $ZERROR as any string up to 128 characters. Usually you would set $ZERROR to a null string, but you can set $ZERROR to a value.

Unstacking NEW Commands With Error Traps

When an error trap occurs and the program stack entries are removed, Caché also removes all stacked NEW commands back to the subroutine level containing the SET $ZTRAP=. However, all NEW commands executed at that subroutine level remain, regardless of whether they were added to the stack before or after $ZTRAP was set.

For example:

Main
  SET A=1,B=2,C=3,D=4,E=5,F=6
  NEW A,B
  SET $ZTRAP="ErrSub"
  NEW C,D
  DO Sub1
  RETURN
Sub1()
  NEW E,F
  WRITE 6/0    // Error: division by zero
  RETURN
ErrSub()
  WRITE !,"Error is: ",$ZERROR
  WRITE
  RETURN

When the error in Sub1 activates the error trap, the former values of E and F stacked in Sub1 are removed, but A, B, C, and D remain stacked.

$ZTRAP Flow of Control Options

After a $ZTRAP error handler has been invoked to handle an error and has performed any cleanup or error logging operations, the error handler has three flow control options:

  • Handle the error and continue the application.

  • Pass control to another error handler

  • Terminate the application

Continuing the Application

After a $ZTRAP error handler has handled an error, you can continue the application by issuing a GOTO. You do not have to clear the values of the $ZERROR or $ECODE special variables to continue normal application processing. However, you should clear $ZTRAP (by setting it to the empty string) to avoid a possible infinite error handling loop if another error occurs. See “Handling Errors in an Error Handler” for more information.

After completing error processing, your $ZTRAP error handler can use the GOTO command to transfer control to a predetermined restart or continuation point in your application to resume normal application processing.

When an error handler has handled an error, the $ZERROR special variable is set to a value. This value is not necessarily cleared when the error handler completes. The $ZERROR value is overwritten when the next error occurs that invokes an error handler. For this reason, the $ZERROR value should only be accessed within the context of an error handler. Accessing $ZERROR in any other context does not produce reliable results.

Passing Control to Another Error Handler

If the error condition cannot be corrected by a $ZTRAP error handler, you can use a special form of the ZTRAP command to transfer control to another error handler. The command ZTRAP $ZERROR re-signals the error condition and causes Caché to unwind the call stack to the next call stack level with an error handler. After Caché has unwound the call stack to the level of the next error handler, processing continues in that error handler. The next error handler may have been set by either a $ZTRAP or a $ETRAP.

The following figure shows the flow of control in $ZTRAP error handling routines.

$ZTRAP Error Handlers
generated description: errors ztraphandler

Handling Errors with $ETRAP

When an error trap occurs and you have set $ETRAP, Caché takes the following steps:

  1. Sets the values of $ECODE and $ZERROR.

  2. Processes the commands that are the value of $ETRAP.

By default, each DO, XECUTE, or user-defined function context inherits the $ETRAP error handler of the frame that invoked it. This means that the designated $ETRAP error handler at any context level is the last defined $ETRAP, even if that definition was made several stack levels down from the current level.

$ETRAP Error Handlers

The $ETRAP special variable can contain one or more ObjectScript commands that are executed when an error occurs. Use the SET command to set $ETRAP to a string that contains one or more Caché commands that transfer control to an error-handling routine. This example transfers control to the LogError code label (which is part of the routine ErrRoutine):

 SET $ETRAP="DO LogError^ErrRoutine"

The commands in the $ETRAP special variable are always followed by an implicit QUIT command. The implicit QUIT command quits with a null string argument when the $ETRAP error handler is invoked in a user-defined function context where a QUIT with arguments is required.

$ETRAP has a global scope. This means that setting $ETRAP should usually be preceded by NEW $ETRAP. Otherwise, if the value of $ETRAP is set in the current context, then, after passing beyond the scope of that context, the value stored in $ETRAP is still present while control is in a higher-level context. Thus, if you do not specify the NEW $ETRAP, then $ETRAP could be executed at an unexpected time when the context that set that it no longer exists.

See the $ETRAP special variable in the Caché ObjectScript Reference for details.

Context-specific $ETRAP Error Handlers

Any context can establish its own $ETRAP error handler by taking the following steps:

  1. Use the NEW command to create a new copy of $ETRAP.

  2. Set $ETRAP to a new value.

If a routine sets $ETRAP without first creating a new copy of $ETRAP, a new $ETRAP error handler is established for the current context, the context that invoked it, and possibly other contexts that have been saved on the call stack. Therefore InterSystems recommends that you create a new copy of $ETRAP before you set it.

Keep in mind that creating a new copy of $ETRAP does not clear $ETRAP. The value of $ETRAP remains unchanged by the NEW command.

The figure below shows the sequence of $ETRAP assignments that create the stack of $ETRAP error handlers. As the figure shows:

  • Routine A creates a new copy of $ETRAP, sets it to “GOTO ^ERR”, and contains the DO command to call routine B.

  • Routine B does nothing with $ETRAP (thereby inheriting the $ETRAP error handler of Routine A) and contains the DO command to call routine C.

  • Routine C creates a new copy of $ETRAP, sets it to “GOTO ^CERR”, and contains the DO command to call routine D.

  • Routine D creates a new copy of $ETRAP and then clears it, leaving no $ETRAP error handler for its context.

If an error occurs in routine D (a context in which no $ETRAP error handler is defined), Caché removes the DO frame for routine D from the call stack and transfers control to the $ETRAP error handler of Routine C. The $ETRAP error handler of Routine C, in turn, dispatches to ^CERR to process the error. If an error occurs in Routine C, Caché transfers control to the $ETRAP error handler of Routine C, but does not unwind the stack because the error occurs in a context where a $ETRAP error handler is defined.

$ETRAP Error Handlers
generated description: errors etraphandlers

$ETRAP Flow of Control Options

When the $ETRAP error handler has been invoked to handle an error and perform any cleanup or error-logging operations, it has the following flow-of-control options:

  • Handle the error and continue the application.

  • Pass control to another error handler.

  • Terminate the application.

Handling the Error and Continuing the Application

When a $ETRAP error handler is called to handle an error, Caché considers the error condition active until the error condition is dismissed. You dismiss the error condition by setting the $ECODE special variable to the null string:

 SET $ECODE=""

Clearing $ECODE also clears the error stack for your process.

Typically, you use the GOTO command to transfer control to a predetermined restart or continuation point in your application after the error condition is dismissed. In some cases, you may find it more convenient to quit back to the previous context level after dismissing the error condition.

Passing Control to Another Error Handler

If the error condition is not dismissed, Caché passes control to another error handler on the call stack when a QUIT command terminates the context at which the $ETRAP error handler was invoked. Therefore, you pass control to a previous level error handler by performing a QUIT from a $ETRAP context without clearing $ECODE.

If routine D, called from routine C, contains an error that transfers control to ^CERR, the QUIT command in ^CERR that is not preceded by setting $ECODE to "" (the empty string) transfers control to the $ETRAP error handler at the previous context level. In contrast, if the error condition is dismissed by clearing $ECODE, a QUIT from ^CERR transfers control to the statement in routine B that follows the DO ^C command.

Terminating the Application

If no previous level error handlers exist on the call stack and a $ETRAP error handler performs a QUIT without dismissing the error condition, the application is terminated. In Application Mode, Caché is then run down and control is passed to the operating system. The Terminal prompt then appears.

Keep in mind that you use the QUIT command to terminate a $ETRAP error handler context whether or not the error condition is dismissed. Because the same $ETRAP error handler can be invoked at context levels that require an argumentless QUIT and at context levels (user-defined function contexts) that require a QUIT with arguments, the $QUIT special variable is provided to indicate the QUIT command form required at a particular context level.

The $QUIT special variable returns 1 (one) for contexts that require a QUIT with arguments and returns 0 (zero) for contexts that require an argumentless QUIT.

A $ETRAP error handler can use $QUIT to provide for either circumstance as follows:

  Quit:$QUIT "" Quit

When appropriate, a $ETRAP error handler can terminate the application using the HALT command.

Handling Errors in an Error Handler

When an error occurs in an error handler, the flow of execution depends on the type of error handler that is currently executing.

Errors in a $ETRAP Error Handler

If the new error occurs in a $ETRAP error handler, Caché unwinds the call stack until the context level at which the $ETRAP error handler was invoked has been removed. Caché then passes control to the next error handler (if any) on the call stack.

Errors in a $ZTRAP Error Handler

If the new error occurs in a $ZTRAP error handler, Caché passes control to the first error handler it encounters, unwinding the call stack only if necessary. Therefore, if the $ZTRAP error does not clear $ZTRAP at the current stack level and another error subsequently occurs in the error handler, the $ZTRAP handler is invoked again at the same context level, causing an infinite loop. To avoid this, Set $ZTRAP to another value at the beginning of the error handler.

Error Information in the $ZERROR and $ECODE Special Variables

If another error occurs during the handling of the original error, information about the second error replaces the information about the original error in the $ZERROR special variable. However, Caché appends the new information to the $ECODE special variable. Depending on the context level of the second error, Caché may append the new information to the process error stack as well.

If the existing value of the $ECODE special variable is non-null, Caché appends the code for the new error to the current $ECODE value as a new comma piece. Error codes accrue in the $ECODE special variable until either of the following occurs:

  • You explicitly clear $ECODE, for example:

     SET $ECODE = ""
  • The length of $ECODE exceeds the maximum string length.

Then, the next new error code replaces the current list of error codes in $ECODE.

When an error occurs and an error stack already exists, Caché records information about the new error at the context level where the error occurred, unless information about another error already exists at that context level on the error stack. In this case, the information is placed at the next level on the error stack (regardless of the information that may already be recorded there).

Therefore, depending on the context level of the new error, the error stack may extend (one or more context levels added) or information at an existing error stack context level may be overwritten to accommodate information about the new error.

Keep in mind that you clear your process error stack by clearing the $ECODE special variable.

See the $ECODE and $ZERROR special variables in the Caché ObjectScript Reference for details. For further information on handling $ZERROR errors, refer to the %SYSTEM.ErrorOpens in a new tab class methods in the InterSystems Class Reference.

Forcing an Error

You set the $ECODE special variable or use the ZTRAP command to cause an error to occur under controlled circumstances.

Setting $ECODE

You can set the $ECODE special variable to any non-null string to cause an error to occur. When your routine sets $ECODE to a non-null string, Caché sets $ECODE to the specified string and then generates an error condition. The $ZERROR special variable in this circumstance is set with the following error text:

<ECODETRAP>

Control then passes to error handlers as it does for normal application-level errors.

You can add logic to your error handlers to check for errors caused by setting $ECODE. Your error handler can check $ZERROR for an <ECODETRAP> error (for example, “$ZE["ECODETRAP"”) or your error handler can check $ECODE for a particular string value that you choose.

Creating Application-Specific Errors

Keep in mind that the ANSI Standard format for $ECODE is a comma-surrounded list of one or more error codes:

  • Errors prefixed with “Z” are implementation-specific errors

  • Errors prefixed with “U” are application-specific errors

You can create your own error codes following the ANSI Standard by having the error handler set $ECODE to the appropriate error message prefixed with a “U”.

  SET $ECODE=",Upassword expired,"

Processing Errors from the Terminal Propmt

When you generate an error after you sign onto Caché at the Terminal prompt with no error handler set, Caché takes the following steps when an error occurs in a line of code you enter:

  1. Caché displays an error message on the process’s principal device.

  2. Caché breaks at the call stack level where the error occurred.

  3. The process returns the Terminal prompt.

Understanding Error Message Formats

As an error message, Caché displays three lines:

  1. The entire line of source code in which the error occurred.

  2. Below the source code line, a caret (^) points to the command that caused the error.

  3. A line containing the contents of $ZERROR.

In the following Terminal prompt example, the second SET command has an undefined local variable error:

USER>WRITE "hello",! SET x="world" SET y=zzz WRITE x,!
hello

WRITE "hello",! SET x="world" SET y=zzz WRITE x,!
                              ^
<UNDEFINED> *zzz
USER>

In the following example, the same line of code is in a program named mytest executed from the Terminal prompt:

USER>DO ^mytest
hello

  WRITE "hello",! SET x="world" SET y=zzz WRITE x,!
                                ^
<UNDEFINED>WriteOut+2^mytest *zzz
USER 2d0>

In this case, $ZERROR indicates that the error occurred in mytest at an offset of 2 lines from the a label named WriteOut. Note that the prompt has changed, indicating that a new program stack level has been initiated.

Understanding the Terminal Prompt

By default, the Terminal prompt specifies the current namespace. If one or more transactions are open, it also includes the $TLEVEL transaction level count. This default prompt can be configured with different contents, as described in the ZNSPACE command documentation. The following examples show the defaults:

USER>
TL1:USER>

If an error occurs during the execution of a routine, the system saves the current program stack and initiates a new stack frame. An extended prompt appears, such as:

USER 2d0>

This extended prompt indicates that there are two entries on the program stack, the last of which is an invoking of DO (as indicated by the “d”). Note that this error placed two entries on the program stack. The next DO execution error would result in the Terminal prompt:

USER 4d0>

For a more detailed explanation, refer to Terminal Prompt Shows Program Stack Information in the “Command-line Routine Debugging” chapter.

Recovering from the Error

You can then take any of the following steps:

  • Issue commands from the Terminal prompt

  • View and modify your variables and global data

  • Edit the routine containing the error or any other routine

  • Execute other routines

Any of these steps can even cause additional errors.

After you have taken these steps, your most likely course is to either resume execution or to delete all or part of the program stack.

Resuming Execution at the Next Sequential Command

You can resume execution at the next command after the command that caused the error by entering an argumentless GOTO at the Terminal prompt:

USER>DO ^mytest
hello

  WRITE "hello",! SET x="world" SET y=zzz WRITE x,!
                                ^
<UNDEFINED>WriteOut+2^mytest *zzz
USER 2d0>GOTO
world

USER>
Resuming Execution at Another Line

You can resume execution at another line by issuing a GOTO with a label argument at the Terminal prompt:

USER 2d0>GOTO ErrSect
Deleting the Program Stack

You can delete the entire program stack by issuing an argumentless QUIT command at the Terminal prompt:

USER 4d0>QUIT
USER>
Deleting Part of the Program Stack

You can issue QUIT n with an integer argument at the Terminal prompt to delete the last (or last several) program stack entry:

USER 8d0>QUIT 1
 
USER 7E0>QUIT 3
 
USER 4d0>QUIT 1
 
USER 3E0>QUIT 1
 
USER 2d0>QUIT 1
 
USER 1S0>QUIT 1
 
USER>

Note that in this example because the program error created two program stack entries, you must be on a “d” stack entry to resume execution by issuing a GOTO. Depending on what else has occurred, a “d” stack entry may be even-numbered (USER 2d0>) or odd-numbered (USER 3d0>).

By using NEW $ESTACK, you can quit to a specified program stack level:

USER 4d0>NEW $ESTACK

USER 5E1>
/* more errors create more stack frames */

USER 11d7>QUIT $ESTACK
 
USER 4d0>

Note that the NEW $ESTACK command adds one entry to the program stack.

Logging Application Errors

Caché provides several ways to log an exception to an application error log.

Using %ETN to Log Application Errors

The %ETN utility logs an exception to the application error log and then exits. You can invoke %ETN (or one of its entrypoints) as a utility:

  DO ^%ETN

Or you can set the $ZTRAP special variable to %ETN (or one of its entrypoints):

  SET $ZTRAP="^%ETN"

You can specify %ETN or one of its entry points:

  • FORE^%ETN (foreground) logs an exception to the standard application error log, and then exits with a HALT. This invokes a rollback operation. This is the same operation as %ETN.

  • BACK^%ETN (background) logs an exception to the standard application error log, and then exits with a QUIT. This does not invoke a rollback operation.

  • LOG^%ETN logs an exception to the standard application error log, and then exits with a QUIT. This does not invoke a rollback operation. The exception can be a standard %Exception.SystemExceptionOpens in a new tab, or a user-defined exception.

    To define an exception, set $ZERROR to a meaningful value prior to calling LOG^%ETN; this value will be used as the Error Message field in the log entry. You can also specify a user-defined exception directly into LOG^%ETN: DO LOG^%ETN("This is my custom exception"); this value will be used as the Error Message field in the log entry. If you set $ZERROR to the null string (SET $ZERROR="") LOG^%ETN logs a <LOG ENTRY> error. If you set $ZERROR to <INTERRUPT> (SET $ZERROR="<INTERRUPT>") LOG^%ETN logs an <INTERRUPT LOG> error.

    LOG^%ETN returns a %List structure with two elements: the $HOROLOG date and the Error Number.

The following example uses the recommended coding practice of immediately copying $ZERROR into a variable. LOG^%ETN returns a %List value:

  SET err=$ZERROR
  /* error handling code */
  SET rtn = $$LOG^%ETN(err)
  WRITE "logged error date: ",$LIST(rtn,1),!
  WRITE "logged error number: ",$LIST(rtn,2)

Calling LOG^%ETN or BACK^%ETN automatically increases the available process memory, does the work, and then restores the original $ZSTORAGE value. However, if you call LOG^%ETN or BACK^%ETN following a <STORE> error, restoring the original $ZSTORAGE value might trigger another <STORE> error. For this reason, the system retains the increased available memory when these %ETN entrypoints are invoked for a <STORE> error.

Using Management Portal to View Application Error Logs

From the Management Portal, select System Operation, then System Logs, then Application Error Log. This displays the Namespace list of those namespaces that have application error logs. You can use the header to sort the list.

Select Dates for a namespace to display those dates for which there are application error logs, and the number of errors recorded for that date. You can use the headers to sort the list. You can use Filter to match a string to the Date and Quantity values.

Select Errors for a date to display the errors for that date. Error # integers are assigned to errors in chronological order. Error # *COM is a user comment applied to all errors for that date. You can use the headers to sort the list. You can use Filter to match a string.

Select Details for an error to open an Error Details window that displays state information at the time of the error including special variables values and Stacks details. You can specify a user comment for an individual error.

The Namespaces, Dates, and Errors listings include checkboxes that allow you to delete the error log for the corresponding error or errors. Check what you wish to delete, then select the Delete button.

Using %ERN to View Application Error Logs

The %ERN utility examines application errors recorded by the %ETN error trap utility. %ERN returns all errors logged for the current namespace.

Take the following steps to use the %ERN utility:

  1. At the Terminal prompt, DO ^%ERN. The name of the utility is case-sensitive; responses to prompts within the utility are not case-sensitive. At any prompt you may enter ? to list syntax options for the prompt, or ?L to list all of the defined values. You may use the Enter key to exit to the previous level.

  2. For Date: at this prompt, enter the date on which the errors occurred. You can use any date format that is accepted by the %DATE utility; if you omit the year, the current year is assumed. It returns the date and the number of errors logged for that date. Alternative, you can retrieve lists of errors from this prompt using the following syntax:

    • ?L lists all dates on which errors occurred, most recent first, with the number of errors logged. The (T) column indicates how many days ago, with (T) = today and (T-7) = seven days ago. If a user comment is defined for all of the day’s errors, it is shown in square brackets. After listing, it re-displays the For Date: prompt. You can enter a date or T-n.

    • [text lists all errors that contain the substring text. <text lists all errors that contain the substring text in the error name component. ^text lists all errors that contain the substring text in the error location component. After listing, it re-displays the For Date: prompt. Enter a date.

  3. Error: at this prompt supply the integer number for the error you want to examine: 1 for the first error of the day, 2 for the second, and so on. Or enter a question mark (?) for a list of available responses. The utility displays the following information about the error: the Error Name, Error Location, time, system variable values, and the line of code executed at the time of the error.

    You can specify an * at the Error: prompt for comments. * displays the current user-specified comment applied to all of the errors of that day. It then prompts you to supply a new comment to replace the existing comment for all of these errors.

  4. Variable: at this prompt you can specify numerous options for information about variables. If you specify the name of a local variable (unsubscipted or subscripted), %ERN returns the stack level and value of that variable (if defined), and all its descendent nodes. You cannot specify a global variable, process-private variable, or special system variable.

    You may enter ? to list other syntax options for the Variable: prompt.

    • *A: when specified at the Variable: prompt, displays the Device: prompt; press Return to display results on the current Terminal device.

    • *V: when specified at the Variable: prompt, displays the Variable(s): prompt. At this prompt specify an unsubscripted local variable or a comma-separated list of unsubscripted local variables; subscripted variables are rejected. %ERN then displays the Device: prompt; press Return to display results on the current Terminal device. %ERN returns the value of each specified variable (if defined) and all its descendent nodes.

Note:

%ER is a legacy name for %ERN. Their functionality is identical, though %ERN is slightly more efficient.

FeedbackOpens in a new tab