# Runtime errors — Handling errors reported while running programs

## Contents

- Overview
- Error handling functions and commands
- Strategy
- TRAP

- Example

- ERR, ERR$ and ERRX$

- Example
- TRAP INPUT/EDIT and the Esc key

- ONERR...ONERR OFF

- Example
- When to use ONERR OFF
- Multiple ONERRs
- TRAP and ONERR

- RAISE

- Example
- TRAP RAISE

## Overview

OPL may display an error message and stop a running program if certain ‘error’ conditions occur. This may happen because:

- There is a mistake, or bug, in your program, which could not be detected during translation - for example, a calculation has involved a division by zero.
- A problem has occurred which prevents a command or function from working - for example, an `APPEND` command may fail because a disk is full.

Unless you include statements which can handle such errors when they occur, OPL will use its own error handling mechanism. The program will stop and an error message be displayed. The first line gives the names of the procedure in which the error occurred, and the module this procedure is in. The second line is the ‘error message’ — one of the messages listed the OPL error values. If appropriate, you will also see a list of variable names or procedure names causing the error.

If you were editing the module with the Program editor and you ran it from there, you would also be taken back to editing the OPL module, with the cursor at the line where the error occurred.

## Error handling functions and commands

To prevent your program being stopped by OPL when an error occurs, include statements in your program which anticipate possible errors and take appropriate action. The following error handling facilities are available in OPL:

- `TRAP` temporarily suppresses OPL’s error processing.
- `ERR`, `ERR$`, and `ERRX$` find out what kind of error has occurred.
- `ONERR` establishes an error handler which can suppress OPL’s error processing over whole modules.
- `RAISE` can be used to simulate error conditions.

These facilities put you in control and must be used carefully.

## Strategy

You should design the error handling of a program in the same way as the program itself. OPL works best when programs are built up from procedures, and you should design your error handling on the same basis. Each procedure should normally contain its own local error handling:

The error handling statements can then be appropriate to the procedure. For example, a procedure which performs a calculation would have one type of error handling, but another procedure which offers a set of choices would have another.

## TRAP

TRAP can be used with any of these commands: APPEND, BACK, CANCEL, CLOSE, COPY, CREATE, DELETE, ERASE, EDIT, FIRST, gCLOSE, gCOPY, gFONT, gPATT, gSAVEBIT, gUNLOADFONT, gUSE, INPUT, INSERT, LAST, LCLOSE, LOADM, LOPEN, MKDIR, MODIFY, NEXT, OPEN, OPENR, POSITION, PUT, RAISE (see below), RENAME, RMDIR, UNLOADM, UPDATE and USE.

The TRAP command immediately precedes any of these commands, separated from it by a space - for example:

        TRAP INPUT a%

If an error occurs in the execution of the command, the program does not stop, and the next line of the program executes as if there had been no error. Normally you would use ERR on the line after the TRAP to find out what the error was.

### Example

When INPUT is used without TRAP and a text string is entered when a number is required, the display just scrolls up and a `?` is shown, prompting for another entry. With TRAP in front of INPUT, you can handle bad entries yourself:

        PROC trapinp:
              LOCAL profit%
              DO
                    PRINT
                    PRINT "Enter profit",
                    TRAP INPUT profit%
              UNTIL ERR=0
              PRINT "Valid number"
              GET
        ENDP

This example uses the `ERR` function, described next.

## ERR, ERR$ and ERRX$

Section Contents

- Example
- TRAP INPUT/EDIT and the Esc key

When an error occurs in a program, check what number the error was, with the `ERR` function:

        e%=ERR

If `ERR` returns zero, there was no error. The value returned by `ERR` is the number of the last error which occurred it changes when a new error occurs. `TRAP` sets `ERR` to zero if no error occurred. Check the number it returns against the error messages listed in the OPL error values .

The `ERR$` function gives you the message for error number `e%`:

        e$=ERR$(e%)

You can also use `ERR` and `ERR$` together:

        e$=ERR$(ERR)

This returns the error message for the most recent error.

The `ERRX$` function gives you the extended message for the current error:
`e$=ERRX$`.

For example, ‘Error in MODULE\PROCEDURE,EXTERN1,EXTERN2,...’. This is the message which would have been presented as an alert if the error had not been trapped. The use of this function gives the list of missing externals and procedure names when an error has been trapped.

### Example

The lines below anticipate that error number -101 (‘File already open’) may occur. If it does, an appropriate message is displayed.

        TRAP OPEN "main",A,a$
        e%=ERR
        IF e%                        REM Checks for an error
              IF e%=-101
                    PRINT "File is already open!"
              ELSE
                    PRINT ERR$(e%)
              ENDIF
        ENDIF

The inner IF...ENDIF structure displays either the message in quotes if the error was number -101, or the standard error message for any other error.

### TRAP INPUT/EDIT and the Esc key

If in response to a `TRAP INPUT` or `TRAP EDIT` statement, the Esc key is pressed while no text is on the input/edit line, the ‘Escape key pressed’ error (number -114) will be raised. (This error will only be raised if the INPUT or EDIT has been trapped. Otherwise, the Esc key still leaves you editing.)

You can use this feature to enable someone to press the Esc key to escape from editing or inputting a value. For example:

        PROC trapInp:
              LOCAL a%,b%,c%
              PRINT "Enter values."
              PRINT "Press Esc to exit"
              PRINT "a% =", :TRAP INPUT a% :PRINT
              IF ERR=-114 :GOTO end :ENDIF
              PRINT "b% =", :TRAP INPUT b% :PRINT
              IF ERR=-114 :GOTO end :ENDIF
              PRINT "a%*b% =",a%*b%
              PAUSE -40
              RETURN
        end::
              PRINT :PRINT "OK, finishing..."
              PAUSE -40
              RETURN
        ENDP

## ONERR...ONERR OFF

Section Contents

- Example
- When to use ONERR OFF
- Multiple ONERRs
- TRAP and ONERR

`ONERR` sets up an error handler. This means that, whenever an error occurs in the procedure containing `ONERR`, the program will jump to a specified label instead of stopping in the normal way. This error handler is active until an `ONERR OFF` statement.

You specify the label after the word `ONERR`.

The label itself can then occur anywhere in the same procedure - even above the `ONERR` statement. After the label should come the statements handling whatever error may have caused the program to jump there. For example, you could just have the statement `PRINT ERR$(ERR)` to display the message for whatever error occurred.

All statements after the `ONERR` command, including those in procedures called by the procedure containing the `ONERR`, are protected by the `ONERR`, until the `ONERR OFF` instruction is given.

### Example

If an error occurs in the lines between `ONERR errHand` and `ONERR OFF`, the program jumps to the label `errHand::` where a message is displayed.

Always cancel `ONERR` with `ONERR OFF` immediately after the label.

### When to use ONERR OFF

You could protect the whole of your program with a single `ONERR`. However, it’s often easier to manage a set of procedures which each have their own `ONERR`...`ONERR OFF` handlers, each covering their own procedure. Secondly, an endless loop may occur if all errors feed back to the same single label.

For example, the diagram below shows how an error handler is left active by mistake. Two completely different errors cause a jump to the same label, and cause an inappropriate explanatory message to be displayed. In this example an endless loop is created because `next:` is called repeatedly:

### Multiple ONERRs

You can have more than one `ONERR` in a procedure, but only the most recent ONERR is active. Any errors cause a jump to the label for the most recent ONERR.

`ONERR OFF` disables all `ONERR`s in the current procedure. If there are `ONERR`s in other procedures above this procedure (calling procedures) these `ONERR`s are not disabled.

### TRAP and ONERR

`TRAP` has priority over `ONERR`. In other words, an error from a command used with `TRAP` will not cause a jump to the error handler specified with `ONERR`.

## RAISE

Section Contents

- Example
- TRAP RAISE

The `RAISE` command generates an error, in the same way that OPL raises errors whenever it meets certain conditions which it recognises as unacceptable (for example, when invalid arguments are passed to a function). Once an error has been raised, either by OPL itself or by the `RAISE` command, the error-handling mechanism currently in use takes effect - the program will stop and report a message, or if you’ve used ONERR the program will jump to the `ONERR` label.

There are two reasons for using `RAISE`:

- You may want to mimic OPL’s error conditions in your own procedures. For example, if you create a new procedure which performs a calculation and returns a value, you may want to `RAISE` an ‘Overflow’ or ‘Divide by zero’ error if unsuitable numbers are passed as parameters.

In this case, you would `RAISE` one of the standard error numbers. You could handle this yourself with `ONERR`, or let OPL handle it in the normal way.

- OPL raises only a limited range of errors for general use, and you may want to raise new kinds of error codes specific to your program or particular circumstances.

In this case, you would `RAISE` a new error number. With `ONERR` on, `RAISE` would go to the `ONERR` label, where you would have code to interpret your new error numbers. You could then display appropriate messages.

You can use any positive number (from 0 to 127) as a new error code. Do not use any of the numbers in the list that follows.

You may also find `RAISE` useful for testing your error handling.

### Example

        PROC main:
              REM calling procedure
              PRINT myfunc:(0.0)                        REM will raise error -2
        ENDP

        PROC myfunc:(x)
              LOCAL s
              REM returns 1/sqr(x)
              s=SQR(x)
              IF s=0
                    RAISE -2
                    REM ‘Invalid arguments’
                    REM avoids ‘divide by zero’
              ENDIF
              RETURN (1/s)
        ENDP

This uses `RAISE` to raise the ‘Invalid arguments’ error not the ‘Divide by zero’ error, since the former is the more appropriate message.

### TRAP RAISE

`TRAP RAISE err%` can be used to clear the `TRAP` flag and sets `ERR`’s value to `err%`. For example, using `err%=0` will clear `ERR`.