There are 6 types of errors which the REXX interpreter itself automatically checks for and reports. We call these "conditions". The names of these 6 conditions are SYNTAX, NOTREADY, ERROR, FAILURE, HALT, and NOVALUE.

A SYNTAX condition happens when there is some mistake you made in notating one of your instructions, such as putting the UPPER keyword before PARSE, or messing up the quote characters in a literal string. A SYNTAX condition can also happen when you pass an incorrect/unrecognized argument to some Function. SYNTAX condition can also happen when there is some operating system failure (other than a failure concerning streams), such as out of memory.

A NOTREADY condition happens when a stream function has a problem accessing some stream, such as LINEIN() not properly reading the next line from a stream. This may involve a failure on the part of the operating system's underlying file system.

An ERROR condition happens when there is some error that a Function you call in a DLL/EXE, or (non-REXX) program that you launch, encounters as a result of doing its work. For example, if you launch some program that downloads a web page to a file, and it encounters a problem, an ERROR condition may occur.

Some Functions in DLLs/EXEs, or attempts to launch a program, may cause a FAILURE condition instead of an ERROR condition, especially if the problem is more severe, such as that aforementioned program not being found on the system.

A HALT condition happens when the person running your script presses the CTRL and C keys simultaneously to abort script execution, or some Function you call in a DLL/EXE wants your script to abort.

A NOVALUE condition happens when a variable is used without first having been explicitly assigned a value.

At the moment that a condition actually happens, such as a SYNTAX error being encountered, this is referred to as "raising the condition".

REXX does default handling for each of these 6 conditions whenever the condition is raised. For some conditions (such as SYNTAX and HALT), the default handling is to display (in the command prompt window, or Reginald's pop-up message box) some information about the nature of the problem, and immediately end the script at the line where the condition occurred. For other conditions (such as NOVALUE, ERROR, FAILURE, and NOTREADY), the default handling is to do nothing about each and every instance of the condition being raised (so that the script continues executing, with whatever consequences happen as a result of that condition not ever being definitively handled).


Setting your own handler

You have previously seen how the SIGNAL instruction can be used to jump to a specific label in your script. This instruction has another purpose too. The SIGNAL instruction also allows you to replace a condition's default handling with your own REXX instructions. You can specify a label that REXX will automatically jump to whenever a certain condition is raised. This is referred to as "trapping a condition".

Trapping for one of the above, 6 conditions is turned on by putting the keyword ON after SIGNAL, followed by the condition name. By default, the label that REXX jumps to is the same as the condition name. For example, assume you put the following line at the top of your script:

SIGNAL ON SYNTAX
Now, when REXX first encounters a syntax error in your script, REXX will try to jump to a label named SYNTAX somewhere in your script (as opposed to displaying an error message to the command prompt window, and ending your script). Obviously, you should put that label somewhere in your script, perhaps before an EXIT instruction so that the script ends at that point.

For example, you may have these lines at the bottom of your script:

SYNTAX:
   SAY "Syntax error"
   EXIT
Note that the above REXX instructions (sort of) do what REXX's default SYNTAX handling would do. (ie, They display a message and end the script right there, when the condition was raised). But, you could do something different, for example, maybe ask the user if he wants to continue with the script execution, and then jump (ie, SIGNAL) to some other place in the script.

If you want to name your label something other than the same as the condition name, then you can put the NAME keyword followed by the desired label name. Here, we jump to the label MyError when a SYNTAX condition is raised:

SIGNAL ON SYNTAX NAME MyError
Sometimes it is useful to have different names, in case you have more than one handler for a given condition, perhaps for different sections of your script. But, only one handler for a given condition can be turned ON at any point (although each of the 6 conditions can have its own handler ON). For example, here we have two different handlers for NOTREADY (although only one is effective at any given moment):
/* Jump to MyError if the call to LINEIN() fails */
SIGNAL ON NOTREADY NAME MyError
chars = LINEIN(,,1)

/* Jump to MyError2 if the call to LINEOUT() fails */
SIGNAL ON NOTREADY NAME MyError2
err = LINEOUT(,chars)

RETURN

MyError:
   SAY "LINEIN() failed"
   EXIT

MyError2:
   SAY "LINEOUT() failed"
   EXIT
You can turn off your condition handler at any point by putting the keyword OFF after SIGNAL, followed by the condition name. When REXX executes the following instruction, it no longer jumps to some label when a SYNTAX condition is raised. Instead, it again does its default handling for SYNTAX condition.
SIGNAL OFF SYNTAX
You can turn trapping on or off whenever desired. But there is one important caveat to note. When REXX jumps to your handler, it automatically turns your trapping off for that given condition. So, if you choose to continue on with your script, and you want to trap more instances of that particular condition, then you'll need to do another SIGNAL ON instruction. For example, assume that the calls to LINEIN() below will fail, for some reason, and raise a NOTREADY error:
/* Jump to MyError if the call to LINEIN() fails */
SIGNAL ON NOTREADY NAME MyError
chars = LINEIN(,,1)

here:
/* If REXX called MyError, then we'll
 * need to do SIGNAL ON NOTREADY again
 * if we want to trap another NOTREADY.
 */
SIGNAL ON NOTREADY NAME MyError2
chars = LINEIN(,,1)

here2:
RETURN

MyError:
   SAY "LINEIN() failed"
   SIGNAL here

MyError2:
   SAY "LINEIN() failed"
   SIGNAL here2

You can trap any or all conditions, each with a different handler. For example, here we trap any SYNTAX or NOTREADY condition that could be raised as a result of our call to LINEOUT().

SIGNAL ON NOTREADY NAME MyNotready
SIGNAL ON SYNTAX NAME MySyntax
err = LINEOUT(,chars)
RETURN

MySyntax:
   SAY "LINEOUT() failed with SYNTAX"
   EXIT

MyNotready:
   SAY "LINEOUT() failed with NOTREADY"
   EXIT

It is also possible to simultaneously use the same handler for different conditions, as so:

/* Use MyError to simultaneously trap both SYNTAX and NOTREADY */
SIGNAL ON NOTREADY NAME MyError
SIGNAL ON SYNTAX NAME MyError
err = LINEOUT(,chars)
RETURN

MyError:
   SAY "LINEOUT() failed with either SYNTAX or NOTREADY"
   EXIT

CALL versus SIGNAL

In the examples above, we used SIGNAL ON to set our own handler for a condition. Therefore, whenever that condition is raised, the REXX script jumps to the specified label -- never to return to where it was previously (unless you SIGNAL back to some label).

You can alternately use CALL ON instead of SIGNAL ON. For example:

CALL ON NOTREADY NAME MyError
The difference is that MyError should be a Subroutine that ends with RETURN. What happens as a result of the above instruction? When a NOTREADY condition is raised, REXX will call MyError() just like any other subroutine in your script, and then when it encounters MyError()'s RETURN, REXX resumes execution after the instruction which raised the condition. Using CALL ON therefore allows you to perform some condition handling and then resume the script from the line after where the condition occurred.
/* Call MyError if the call to LINEIN() fails */
CALL ON NOTREADY NAME MyError
chars = LINEIN(,,1)

/* If NOTREADY occurs on the line above, then REXX
 * effectively sticks a "CALL MyError" line right here.
 */

here:

EXIT

MyError:
   SAY "LINEIN() failed"
   RETURN /* Returns to the label "here" */
There is another important difference between SIGNAL ON and CALL ON. With SIGNAL ON, when REXX jumps to your handler, it turns trapping off for that condition. With CALL ON, REXX does not turn trapping off. So you do not need to issue another CALL ON instruction in order to reinstate your trapping of the condition. But this also means that if you want to turn trapping off for that condition, you must explicitly use a CALL OFF (or SIGNAL OFF) instruction.

Most REXX interpreters allow your choice of CALL ON or SIGNAL ON to be used only with the ERROR, FAILURE, HALT, NOTREADY, (and perhaps NOVALUE) conditions. The other conditions must use SIGNAL ON.

Note: Reginald allows CALL ON to be used with all conditions.


SIGL variable

Whenever a condition occurs, a special variable named SIGL is set to the line number of the instruction where the condition was raised.

Note: The SIGL variable is created by the REXX interpreter, which assigns it a value when a condition is raised. You should never name your own variable SIGL, but may reference this variable.

/* This script goes on forever until the user stops it */
SAY " Press CTRL and C keys simultaneously to halt"
SIGNAL ON HALT
DO i=1
   SAY i
   DO 10000
   END
END
EXIT

HALT:
   /* Use the SIGL variable's value to tell upon
    * which line the HALT condition was raised.
    */
   SAY "Died at line" SIGL

CONDITION() built-in function

The CONDITION() built-in function gives a lot of information about the currently raised condition. The CONDITION() function is primarily called from within your own handler.

For example, CONDITION('C') can be used to query what condition has been raised (ie, SYNTAX, NOTREADY, NOVALUE, etc). This may be useful if you use the same handler to deal with different condition types.

CONDITION('E') can return an error number associated with that instance of the condition. This number may help you deduce exactly why the condition was raised.

Note: Some REXX interpreters do not implement the 'E' option for CONDITION(). In this case, you'll have to make due with the 'D' option only.

CONDITION('D') may return a descriptive error message that you can display to the person running your script to tell him why the condition was raised. (Or it may return some other string that ultimately can be used to create such an error message).

Note: Reginald's CONDITION() function has an addition option. CONDITION('M') will present a pop-up message box containing the error message, the name of the script in which the condition was raised, the source line and line number upon which the condition was raised, and a Help button to bring up a help page for that error. This is preferable to trying to create your own message to present to the person running your script.

Note: CONDITION() keeps track of only the currently raised condition, so if another condition happens to be raised within your handler, then CONDITION() will return information about that second condition. So too, the SIGL variable will change when another condition is raised.


Trapping in REXX functions/subroutines

When you call some function or subroutine contained within your own script, any trapping that you've turned on prior to your call into the subroutine, is applied to that subroutine. So, for example, if you turn on NOTREADY trapping, and then you call some subroutine, and some line in that subroutine raises the NOTREADY condition, then your NOTREADY handler will be executed.

/* Call MyError if NOTREADY is raised */
CALL ON NOTREADY NAME MyError

/* Call a subroutine within our script */
CALL MySub

EXIT

MyError:
   SAY "NOTREADY condition is raised"
   RETURN

MySub:
   chars = LINEIN(,,1)

   /* If NOTREADY occurs on the line above, then REXX
    * effectively sticks a "CALL MyError" line right
    * here, and then resumes at the line below.
    */

   RETURN
Some interpreters, such as Reginald, even extend this trapping to include child scripts that you call. For example, if you turn on Reginald's TRAP option (by putting the instruction OPTIONS 'TRAP' in your script) and trap HALT condition in your script, and then you call some other script (which doesn't trap HALT), and some line in that second script raises a HALT condition, then the HALT handler in your own script gets executed.

Consider the following script named abort:

/* Here's a REXX script we'll be calling. Its name is abort().
   All it does is loop forever until the user initiates a HALT. And
   since HALT is not trapped here, this script aborts then.
*/
SAY "Press CTRL-C to abort..."
DO FOREVER
END
RETURN
Now consider if we run the following script which calls abort:
/* Test if a parent script can trap an untrapped HALT in a child script */
OPTIONS 'TRAP'
SIGNAL ON HALT NAME MyHalt
CALL abort
SAY "ERROR: Parent did not trap the untrapped HALT in child"
EXIT

MyHalt:
   SAY "Parent successfully trapped the untrapped HALT in child"
   RETURN
The "abort" script does not display any information to the command prompt window when it is aborted. Instead, since the script that called "abort" is trapping HALT, then "abort" terminates, and MyHalt gets executed. You therefore never see the message "ERROR: Parent did not trap the untrapped HALT in child". You do see the message "Parent successfully trapped the untrapped HALT in child".

SIGL will be set to the line number in the parent script where "abort" was called (ie, line 4). CONDITION('E') will return an error number of 65. This number means that you trapped some condition that was raised in the script you called, but that child script didn't trap the condition for itself. CONDITION('D') will return an error message that mentions that the failure occurred in the script named "abort" and will also mention the line number within "abort" where the condition was raised, and why the condition was raised.

But if desired, each subroutine/function can have its own unique handlers for the 6 conditions. What this means is that, for example, if you trap NOTREADY condition in a parent level, and then you call a subroutine that also traps NOTREADY, the child's trapping of NOTREADY can be unique, and does not affect the parent's trapping. Consider the following:

/* Call MyError if the call to LINEIN() fails */
CALL ON NOTREADY NAME MyError
chars = LINEIN(,,1)

/* If NOTREADY occurs on the line above, then REXX
 * effectively sticks a "CALL MyError" line right here.
 */

/* Call a subroutine within our script */
CALL MySub

/* Now that we're back here, our
 * "CALL ON NOTREADY NAME MyError" is
 * restored. Therefore, if the following
 * line raises a NOTREADY condition, REXX
 * effectively sticks another "CALL MyError"
 * line right after it.
 */
chars = LINEIN(,,1)

EXIT

MyError:
   SAY "LINEIN() failed in main body of script"
   RETURN

MySub:
   /* Let's trap NOTREADY with SIGNAL ON
    * and set our handler to the label MyError2
    */
   SIGNAL ON NOTREADY NAME MyError2
   chars = LINEIN(,,1)
   RETURN

MyError2:
   SAY "LINEIN() failed in MySub()"
   RETURN
So too, each script you call can have its own unique handlers for the 6 conditions, and these do not affect your own script's handlers.

Errata

The next sections contain details about each condition, and how the CONDITION() 'D' and 'E' options can be used in your handler for that condition.