Chapter 6 – Dealing with Runtime Exceptions
By now you have a good idea of how InternetBasic programs are organized and how they store and retrieve data. By studying the sample programs in the previous chapters, you’ve also seen some of the ways that IB programs deal with runtime exceptions. This chapter provides you with a better understanding of the events that cause runtime exceptions and the steps your program can take to recover from them.
To review, a runtime exception occurs when a program executes an instruction that results in an unexpected or extraordinary outcome. Here are some examples:
As you can see, runtime exceptions typically occur when input/output statements are executed.
Rather than stop a program when an exception occurs, the IB runtime system informs it about the exception and provides ways for it to recover. Information is available via a system variable named EXCP. This variable contains a number that corresponds to the exception that occurred. For example, when an end-of-file exception happens, EXCP is set to 2. When a key-not-found exception occurs, EXCP is set to 32. All of the IB runtime exception codes are described below.
To recover from exceptions, an IB program can branch to a specific statement label when an exception happens (in other words, jump to some location in the code that is out of sequence from where the program was when the exception occurred). Such locations mark the beginning of “exception routines.” By testing the value of the EXCP system variable, an exception routine can determine what exception occurred and then decide when action to take (such as displaying a message for the user, finding the next matching record, etc.). Some of these techniques have already been demonstrated in the sample programs.
Of course, the program does not have to include exception routines, in which case a runtime exception will cause the program to halt (and will also cause the IB runtime exception reporting program to run).
There are several ways to include exception routine branching instructions in IB programs. The most common is:
statement,
EXCP=statement-label
Where the EXCP= parameter provides the statement-label where this program branches if an exception occurs when this statement is executed. (If no exception occurs, the program does not branch anywhere; it simply processes the next instruction.)
An alternative is to have the program branch to a subroutine when an exception occurs, which is written like this:
statement,
EXCPSUB=statement-label
Another alternative is to set a default exception path, one that is used if no specific EXCP= or EXCPSUB= parameter is included for a particular statement. This is written as follows:
ERRORTO
statement-label
This statement has an alternative form, which is:
ERRORTO
SYSTEM
This version of ERRORTO clears the previous ERRORTO statement-label that was in effect.
Finally, the default exception path can be a subroutine, in which case the statement is:
ERRORSUB
statement-label
And, as you might have expected, the way to clear the previously set ERRORSUB label is:
ERRORSUB
SYSTEM
In an exception routine, there are several ways to decide what action to take. We’ve already mentioned the EXCP system variable, but here are two other statements that are useful. First, here’s a statement that jumps back to the instruction where the exception occurred:
AGAIN
Take care when using the AGAIN statement, as it could result in a program getting stuck in an endless loop (assuming that the offending statement keeps generating the same runtime exception time after time).
Second, after testing for the anticipated exceptions, a program may want to deal with unanticipated ones. The following statement, which typically appears at the logical ending of an exception routine, halts your program and runs the IB runtime exception reporting program:
ERROR
The exception reporting program provides information about the exception and where it occurred. This tool also provides a way for you to create a log of all IB runtime exceptions, which can serve as a valuable diagnostic tool for system managers. (See Chapter ??? for more information.)
At the risk of being redundant, here’s a summary of the exception processing options:
Trapping exceptions
Handling exceptions
As previously mentioned, the value of the EXCP system variable equates to a specific runtime exception. Here is a list of the EXCP values and a description of each exception. Values less than 70 are considered to be non-fatal exceptions (i.e., they can be trapped by an IB program).
EXCP |
Description of exception |
0 |
No exception reported |
1 |
Not in Current Use |
2 |
End of file This exception happens when a READ, INQUIRE, or EXTRACT statement attempts to move the file pointer past the last record (text or sequential file) or last key (keyed file). This is the normal consequence or reading through a file in sequential or key-sequential order until the program reaches the last record. If the program tries to read one more record (key), this exception occurs. This exception also happens if the PREV function is executed when the pointer is at the first key in a keyed file (i.e., the program attempts to retrieve a key prior to the first key). |
3 |
Unable to Allocate Space for File |
4 |
Keyed File Write Without Index This exception happens when a WRITE statement attempts to write a record to a keyed file without specifying a key value. |
5 |
Create Without Directory Label This exception happens when a CREATE statement attempts to create a text, sequential, or keyed file without specifying a directory label. |
6 |
Directory Not Found on ACCESS This exception happens when the ACCESS statement attempts to access an IB directory than does not exist. |
7 |
File Unavailable--Lock/Unlock Error This exception happens when an OPEN statement attempts to open a text, sequential, or keyed file that has been opened and locked by another IB program/user. |
8, 9 |
Not in Current Use |
10 |
File not available |
11 |
File Not Found This exception happens when an OPEN statement attempts to open a text, sequential, or keyed file and the specified filename is not found. This is mostly likely doe to the fact that the named file does not exist, or that the file cannot be found among the configured and accessed IB directories. |
12 |
File Already Exists This exception happens when a CREATE statement attempts to create a text, sequential, or keyed file and the specified filename already exists. |
13 |
DOS file already exists |
14-23 |
Not in Current Use |
24 |
Unable to Rename Disk to Disk |
25 |
Invalid DOS Call |
26 |
Invalid Message Call |
27 |
DOS Returned an Error |
28 |
Invalid Priority Call |
29 |
Communications line error |
30 |
Device Inoperative This exception happens when an OPEN statement attempts to open a device (such as a printer) that is not operative. |
31 |
Device or Directory Unavailable This exception happens when an OPEN statement attempts to open a device or directory that is unavailable. |
32 |
Key Not Found This exception happens when a READ, INQUIRE, or EXTRACT statement attempts to retrieve a key/record from a keyed file and the specified key is not found in the key tree. The exception also happens when a REWRITE statement attempts to rewrite a key/record to a keyed file and the specified key does not exist. When using REWRITE, only existing keys may be used. |
33 |
Record Unavailable--Extract Error This exception happens when a READ or EXTRACT statement attempts to retrieve a key/record from a keyed file and the specified key/record is locked (extracted) by another IB program/user. (Note: This exception does not happen with the INQUIRE statement.) |
34 |
Logical Unit # Unavailable for Open This exception happens when an OPEN statement attempts to open a file or device using a logical unit number that has previously been opened. |
35 |
File Not Open for Read or Write This exception happens when a file-oriented input/output statement attempts to read or write to a text, sequential, or keyed file that is not open. |
36 |
Can't Perform Function on Open File This exception happens when a program attempts to perform a function on an open file that is not allowed (e.g., trying to rename an open file). |
37 |
Not in Current Use |
38 |
Device or Target File System Unable to Perform Function |
39 |
Not in Current Use |
40 |
Invalid Window Specified |
41 |
Edit Mask Length Incorrect This exception happens when the length of an edit mask in a FORMAT statement does not match the length/precision of the numeric value being printed. |
42 |
Not in Current Use |
43 |
File extension not allowed for this operation |
44 |
I/O Buffer Overflow This exception happens when a read/write operation is attempted with too many bytes, thus exceeding the input/output buffer size. |
45 |
Invalid Parameter |
46 |
Non-Numeric Input in a Numeric Field This exception happens when an input operation (such as READ, INQUIRE, or EXTRACT) attempts to retrieve a non-numeric character using a numeric variable. This usually indicates a problem with the FORMAT statement (e.g., variables in the wrong order, variables specified with the wrong length/precision), or a problem with the data being retrieved (e.g., fields were written in the wrong order, or written with the wrong length/precision). |
47 |
Parameter Too Large |
48 |
Invalid Key or Record Size: CREATE This exception happens when the CREATE statement attempts to create a file using an invalid key size or record size. |
49 |
Invalid Logical Unit Number This exception happens when the program specifies an invalid logical unit number. Valid logical unit numbers range from 0 through 49. |
50 |
Array Subscript Out of Range This exception happens when a subscript of an array is specified beyond the boundaries of the array. |
51, 52 |
Not in Current Use |
53 |
Invalid File Access This exception happens when a file is access using an invalid method (e.g., specifying a key for a text file). |
54 |
Invalid Function for Named File |
55 |
Not in Current Use |
56 |
Record Already Exists for INSERT This exception happens if an INSERT statement attempts to insert a duplicate key/record to a keyed file. When using INSERT, only new key/records are allowed. |
57-59 |
Not in Current Use |
60 |
Cannot IPL while other Users Active This exception happens if a user attempts to stop the IB runtime system while at least one other IB program is still running. |
61 |
Program Not Found The exception happens when a RUN, ENTER, or ACTIVATE statement attempts to execute a program that is not found in the IB directories that are currently accessed. |
62 |
Program too large |
63 |
Cannot Run Non-Object File The exception happens when a RUN, ENTER, or ACTIVATE statement attempts to execute a file that is not an IB object program. |
64 |
Invalid Partition Name This exception happens when a statement or function refers to a partition name that is invalid. |
65 |
Partition Busy, Program Running This exception happens when an ACTIVATE statement attempts to execute an IB program is a partition where one is already executing. |
66 |
Performed an EXIT when ENTERLEVEL value was 0 This exception happens when the EXIT statement is attempted when the ENTERLEVEL system variable equals 0, which indicates that the current program is not executing as a subprogram. See Chapter ??? for more information about IB subprograms. |
67 |
Partition Busy, Normal Termination |
68 |
Terminate Complete, Task Wasn't Idle |
69 |
Activate Unsuccessful/Task had Error Pending |
Values of EXCP that are greater than or equal to 70 are considered to be fatal exceptions. An IB program cannot trap these exceptions. When they occur, the IB program halts and the exception reporting program takes over, displaying (and optionally, logging) the fatal exception.
EXCP |
Fatal exception |
70 |
ESCAPETO/SUB not active for INTERRUPT |
71 |
DOS Failed to Transfer Read Data |
72 |
IRQ 3 Failure from Comet Board |
73 |
Illegal Jump to Location Zero |
74 |
Bad function call to moveable segment |
75 |
Resources Left Allocated After Call |
76 |
File Size Limit Reached in Demo Mode |
77 |
Attempted Write with Zero Length |
78 |
Error Performing ENTER/EXIT Program |
79 |
Key Block Offset Too Large |
7A |
Attempt to EXTRACT Keyed Only File |
7B |
Maximum File Size Exceeded |
80 |
Maximum I/O Buffer Size Exceeded |
81 |
Printer I/O Error |
82 |
Top Key Sector not in Delete Stack |
83 |
DOS Disc Error |
84 |
Key Sector Search Level Impossible |
85 |
Invalid Instruction/Real Parameters |
86 |
File Descriptor List Overflow |
87 |
Unexpected EOF from DOS |
88 |
Keys but NO Records in File |
89 |
Unsupported Reference |
8D |
503 Spool Jobs File Problem - CLEAR Spool and retry. |
8E |
Node Lock Timed Out |
90 |
Disk Full -- DOS Wrote Less Than Expected |
91 |
Invalid/Unsupported System Call |
92 |
GOSUB Stack Underflow |
93 |
GOSUB Stack Overflow |
94 |
Directory Entry Unavailable / CLOSE |
95 |
Next Key Unavailable for Deletion |
96 |
New Key Found during INSERT |
97 |
New First Key found during INSERT |
98 |
Extracted Record not found in Table |
99 |
Error Received from DOS |
9A |
NLM Error in key tree |
9B |
NLM Transport Error (SEND) |
9C |
CometServe ENTER error |
9D |
CometServe EXIT error |
9E |
NLM file system discovered corruption in an I00 file |
9F |
CometServe bad function request |
Summary
This chapter serves as reference material for dealing with runtime exceptions. As you have seen, InternetBasic provides a very easy way to identify and handle these situations. As you write more and more IB programs, you will no doubt develop a personal style for your own exception routines. Keep this chapter handy for those times.
In the next chapter, we’ll take a formal look at the control structures in IB, many of which you’ve already seen in the sample programs.