Explicitly throws an exception to the next exception handler.
Description
The THROW command explicitly throws an exception. An exception can be a system error, a %Status exception, or a user-defined exception. It throws this exception as an object reference (OREF) that inherits from the %Exception.AbstractExceptionOpens in a new tab object. The THROW command throws this exception to the next exception handler.
There are two ways to use THROW oref:
-
TRY/CATCH: Use THROW oref to explicitly signal an exception from within a TRY block of code, transferring execution from the TRY block to its corresponding CATCH block exception handler.
-
Other Exception Handlers: Use THROW oref to explicitly signal an exception when not in a TRY block. This triggers the current exception handler (for example, $ZTRAP), where the oref can be retrieved from the $THROWOBJ special variable.
System Errors
InterSystems IRIS issues a system error when a runtime error occurs, such a referencing an undefined variable. A system error generates a %Exception.SystemExceptionOpens in a new tab object reference, sets the oref properties Code, Name, Location, and Data, and also sets the $ZERROR and $ECODE special variables, and transfers control to the next error handler. This error handler can be a CATCH exception handler, or a $ZTRAP or $ETRAP error handler. A system error is an implicit error, which does not use a THROW.
You can use THROW within an error handler to throw a system error object to a further error handler. This is known as re-signalling a system error.
A THROW passes control up the execution stack to the next error handler. If the exception is an %Exception.SystemExceptionOpens in a new tab object, the next error handler can be either type (CATCH, $ZTRAP, or $ETRAP). Otherwise, there must be a CATCH to handle the exception or InterSystems IRIS generates a <THROW> error.
THROW when not in a TRY Block
If you issue a THROW outside of a TRY block, InterSystems IRIS generates a <THROW> error, such as the following: <THROW>+3^myprog *%Exception.General MyErr 999 My user-defined error. This use of THROW is useful for re-signalling an error.
The object reference (oref) specified in the THROW is stored in the $THROWOBJ special variable. For example, 9@%Exception.General. The $THROWOBJ value is cleared by the next successful THROW operation, or by SET $THROWOBJ="".
In the following example, a THROW throws an exception to a $ZTRAP exception handler:
MainRou
WRITE "In the Main Routine",!!
SET $ZTRAP=^ErrRou
SET mygenex = ##class(%Exception.General).%New("My exception","999",,
"My own special exception")
THROW mygenex
WRITE "This shouldn't display",!
RETURN
ErrRou
WRITE "In $ZTRAP",!
SET oref=$THROWOBJ
SET $THROWOBJ=""
WRITE oref.Name,!
WRITE oref.Code,!
WRITE oref.Data,!
WRITE "End of $ZTRAP",!
RETURN
Examples
The following example uses an instance of the %Exception.GeneralOpens in a new tab class to throw a user-defined exception. It demonstrates how to use THROW with a post-conditional syntax. In this case, if you were to call the Exceptions method and pass in a number greater than or equal to 5 as an argument, an exception would be thrown, causing the WRITE statement in the catch block to be executed.
ClassMethod Exceptions(x As %Integer)
{
set ex = ##class(%Exception.General).%New()
set ex.Name = "Demo Exception",
ex.Code = 100000,
ex.Data = "Tutorial Example"
try {
write !, "Hello!,
throw:(x >= 5) ex // throw the exception }
catch err {
write !, "x: ", ?20, x,
!, "Error name: ", ?20, err.Name,
!, "Error code: ", ?20, err.Code,
!, "Error location: ", ?20, err.Location,
!, "Additional data: ", ?20, err.Data, !
}
write !, "Finished!"
}
Note that $ZCVT(myerr.Name,"O","HTML") is used in the following examples because InterSystems IRIS error names are enclosed in angle brackets and these examples are run from a web browser. In most other contexts, myerr.Name will return the desired value.
The following example generates birth dates in the TRY block; if it generates a birth date that is in the future, it uses THROW to issue a general exception, passing the OREF of the user-defined exception to a general-purpose CATCH block. (You may have to run this example more than once to generate a date that throws an exception):
TRY {
WRITE "In the TRY block",!
SET badDOB=##class(%Exception.General).%New("<BAD DOB>","999",,"Birth date is in the future")
FOR x=1:1:20 { SET rndDOB = $RANDOM(7)_$RANDOM(10000)
IF rndDOB > $HOROLOG { WRITE !,"Birthdate ",$ZDATE(rndDOB,1,,4)," is invalid"
THROW badDOB }
ELSE { WRITE "Birthdate ",$ZDATE(rndDOB,1,,4)," is valid",! }
}
}
CATCH err {
WRITE !,"In the CATCH block"
WRITE !,"Error code=",err.Code
WRITE !,"Error name=",$ZCVT(err.Name,"O","HTML")
WRITE !,"Error data=",err.Data
RETURN
}
The following example can issue either of two THROW commands with a user-defined argument. $RANDOM picks which THROW to issue (random values 0 or 1) or to not issue a THROW (random value 2). Note that code execution continues after the TRY / CATCH block pair, unless block execution ends with a RETURN command:
TRY {
SET errdatazero="this is the zero error"
SET errdataone="this is the one error"
/* Error Randomizer */
SET test=$RANDOM(3)
WRITE "Error test is ",test,!
IF test=0 {
WRITE !,"Throwing exception 998",!
THROW ##class(Sample.MyException).%New("TestZeroError",998,,errdatazero)
THROW myvar
}
ELSEIF test=1 {
WRITE !,"Throwing exception 999",!
THROW ##class(Sample.MyException).%New("TestOneError",999,,errdataone)
}
ELSE { WRITE !,"No THROW error this time" }
}
CATCH exp {
WRITE !,"This is the exception handler"
WRITE !,"Error code=",exp.Code
WRITE !,"Error name=",exp.Name
WRITE !,"Error data=",exp.Data
RETURN
}
WRITE !!,"Execution after TRY block continues here"
The following example shows the use of THROW with a system error. THROW is commonly used in a CATCH exception handler to forward the system error to another handler. This may occur when the system error received is an unexpected type of system error. Note that this requires nesting a TRY block (and corresponding CATCH block) within the CATCH block. It is used to THROW the system error to the nested CATCH block. This is shown in the following example, which calls Calculate to perform a division operation and return the answer. There are three possible outcomes: If y = any non-zero number, the division operation succeeds and no CATCH block code is executed. If y=0 (or any nonnumeric string), the division operation attempts to divide by zero, throwing a system error to its CATCH block; this is caught by the calcerr exception handler, which “corrects” this error and returns a value of 0. If, however, y is not defined (NEW y), calcerr catches an unexpected system error, and throws this error to the myerr exception handler. To demonstrate these three possible outcomes, this sample program uses $RANDOM to set the divisor (y):
Randomizer
SET test=$RANDOM(3)
IF test=0 { SET y=0 }
ELSEIF test=1 { SET y=7 }
ELSEIF test=2 { NEW y }
/* Note: if test=2, y is undefined */
Main
SET x=4
TRY {
SET result=$$Calculate(x,y)
WRITE !,"Calculated value=",result
}
CATCH myerr {
WRITE !,"this is the exception handler"
WRITE !,"Error code=",myerr.Code
WRITE !,"Error name=",$ZCVT(myerr.Name,"O","HTML")
WRITE !,"Error data=",myerr.Data
}
QUIT
Calculate(arg1,arg2) PUBLIC {
TRY {
SET answer=arg1/arg2
}
CATCH calcerr {
WRITE "In the CATCH Block",!
TRY {
IF calcerr.Name="<DIVIDE>" {
WRITE !,"handling zero divide error"
SET answer=0 }
ELSE { THROW calcerr }
RETURN
}
CATCH {
WRITE "Unexpected error",!
WRITE "Error name=",$ZCVT(myerr.Name,"O","HTML"),!
}
}
QUIT answer
}