Using Caché ObjectScript
Error Processing
[Home] [Back] [Next]
InterSystems: The power behind what matters   
Class Reference   
Search:    

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:

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:
Depending on events during execution of the protected statements, one of the following events occurs:
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.AbstractException class, which Caché provides for exception handling. For more information on %Exception.AbstractException, 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:
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.SystemException 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.SystemException and %Exception.AbstractException classes for use with exception handling. %Exception.SystemException inherits from the %Exception.AbstractException class and is used for system errors. For custom errors, create a class that inherits from %Exception.AbstractException. %Exception.AbstractException 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.SystemException 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:
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 %Status data type. For example, the %Save() method, used to save an instance of a %Persistent object, returns a %Status value indicating whether or not the object was saved.
You can use %SYSTEM.Status class methods to inspect and manipulate %Status values.
Caché provides several options for displaying (writing) the %Status 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() method, and other %SYSTEM.Status 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 %Status=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 %Status. %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.Status class.
Creating %Status Errors
You can invoke system-defined %Status errors from your own methods by using the Error() 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() method to create a list of multiple error messages. Then you can use GetOneErrorText() or GetOneStatusText() 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.Error 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.Error 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é in Programmer Mode and have not set an error trap, Caché displays an error message on the principal device and enters Programmer Mode 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:
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:
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
If routine A in the figure above is invoked at the Programmer Mode 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
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:
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 AsSystemError() method of the %Exception.SystemException 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 special variable retains its value until an error occurs or until you set it to a new value.
See the $ZERROR special variable in the Caché ObjectScript Reference for details. For further information on handling $ZERROR errors, refer to the %SYSTEM.Error 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.
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 Caché 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. 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:
For further details see the $ZTRAP special variable in the Caché 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:
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. The $ZERROR special variable retains its value until an error occurs or until you set it to a new value. Usually you would set $ZERROR only 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:
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
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:
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
$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:
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. In Programmer Mode, the Programmer Mode 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:
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.Error 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:
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 in Programmer Mode
When you generate an error after you sign onto Caché in Programmer Mode 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 enters Programmer Mode.
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 we are in Programmer Mode.
Understanding the Programmer Mode 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 program stack entering Programmer Mode. 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 prompt:
USER 4d0>
For a more detailed explanation, refer to Understanding the Programmer Mode Prompt Information in the “Command-line Routine Debugging” chapter.
Recovering from the Error
You can then take any of the following steps:
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 in Programmer Mode:
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 in Programmer Mode:
USER 2d0>GOTO ErrSect
Deleting the Program Stack
You can delete the entire program stack by issuing an argumentless QUIT command in Programmer Mode:
USER 4d0>QUIT
USER>
Deleting Part of the Program Stack
You can issue QUIT n with an integer argument 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.