22 min read

Every COBOL program that runs in production will eventually encounter an error. A file will be missing. A disk will fill up. A record will contain unexpected data. An arithmetic operation will produce a number too large for its destination field. A...

Chapter 16: Declaratives and Exception Handling

Introduction: When Things Go Wrong in Production

Every COBOL program that runs in production will eventually encounter an error. A file will be missing. A disk will fill up. A record will contain unexpected data. An arithmetic operation will produce a number too large for its destination field. A key will not be found in an indexed file. The question is not whether errors will occur, but whether your program is prepared to handle them gracefully.

In many modern programming languages, exception handling was added as an afterthought -- bolted on years after the language was designed. COBOL, by contrast, has had error-handling mechanisms since its earliest standards. The DECLARATIVES section, introduced in COBOL-68, provides a way to intercept I/O errors before they crash a program. The imperative scope terminators introduced in COBOL-85 brought inline error handling with phrases like ON SIZE ERROR, AT END, and INVALID KEY. The COBOL 2002 standard added structured exception handling with RAISE and RESUME, bringing COBOL closer to the try/catch model familiar in Java and C#.

On IBM mainframes, the Language Environment (LE) condition handling model provides an additional layer of error management that operates beneath the COBOL language level, intercepting hardware and software exceptions before they become program-terminating abends. Understanding this model is essential for any COBOL programmer working in a z/OS environment.

This chapter covers the full spectrum of COBOL error handling: from the DECLARATIVES section and file status codes through inline exception phrases, the COBOL 2002 RAISE/RESUME mechanism, IBM-specific condition handling, common abend codes, and defensive programming patterns that prevent errors from reaching production in the first place.


16.1 The DECLARATIVES Section

The DECLARATIVES section is a special area of the PROCEDURE DIVISION that contains error-handling procedures. These procedures are not called directly by your code; instead, they are triggered automatically by the runtime system when specific error conditions occur during file I/O operations.

Syntax and Structure

The DECLARATIVES section must appear at the very beginning of the PROCEDURE DIVISION, before any other sections or paragraphs:

       PROCEDURE DIVISION.
       DECLARATIVES.

       INPUT-ERROR SECTION.
           USE AFTER STANDARD ERROR PROCEDURE ON INPUT-FILE.
       INPUT-ERROR-HANDLER.
           DISPLAY 'I/O ERROR ON INPUT-FILE'
           DISPLAY 'FILE STATUS: ' WS-INPUT-STATUS
           SET WS-INPUT-ERROR TO TRUE
           .

       OUTPUT-ERROR SECTION.
           USE AFTER STANDARD ERROR PROCEDURE ON OUTPUT-FILE.
       OUTPUT-ERROR-HANDLER.
           DISPLAY 'I/O ERROR ON OUTPUT-FILE'
           DISPLAY 'FILE STATUS: ' WS-OUTPUT-STATUS
           SET WS-OUTPUT-ERROR TO TRUE
           .

       END DECLARATIVES.

       MAIN-PROCESSING SECTION.
       0000-MAIN.
           PERFORM 1000-INITIALIZE
           PERFORM 2000-PROCESS
           PERFORM 3000-TERMINATE
           STOP RUN
           .

Key Rules for DECLARATIVES

There are several important rules governing the DECLARATIVES section:

  1. Position: DECLARATIVES must be the first thing after PROCEDURE DIVISION. No other code can precede it.

  2. Section requirement: Each USE statement must be in its own section. The section header provides the name, and the USE statement specifies the condition that triggers it.

  3. END DECLARATIVES: The section ends with the END DECLARATIVES. statement.

  4. No direct PERFORM: You should not PERFORM a declarative section from your main logic. The runtime invokes it automatically.

  5. Scope: When the declarative procedure finishes, control returns to the statement following the I/O statement that caused the error.

  6. File status: The file status variable is set before the declarative procedure executes, so you can examine it within the handler.

The USE Statement Variations

The USE statement specifies when a declarative procedure should be invoked. There are several forms:

      * Triggered by errors on a specific file
       USE AFTER STANDARD ERROR PROCEDURE ON file-name

      * Triggered by errors on any file opened for INPUT
       USE AFTER STANDARD ERROR PROCEDURE ON INPUT

      * Triggered by errors on any file opened for OUTPUT
       USE AFTER STANDARD ERROR PROCEDURE ON OUTPUT

      * Triggered by errors on any file opened for I-O
       USE AFTER STANDARD ERROR PROCEDURE ON I-O

      * Triggered by errors on any file opened for EXTEND
       USE AFTER STANDARD ERROR PROCEDURE ON EXTEND

The keyword EXCEPTION can be used interchangeably with ERROR in the COBOL 2002 standard:

       USE AFTER STANDARD EXCEPTION PROCEDURE ON INPUT-FILE

Priority Rules

When multiple declarative sections could apply to a single error (for example, a file-specific handler and an INPUT handler both existing when an input file encounters an error), the most specific handler takes precedence. A file-specific USE statement overrides a general INPUT/OUTPUT/I-O/EXTEND handler.

A Complete DECLARATIVES Example

       IDENTIFICATION DIVISION.
       PROGRAM-ID. DECLDEMO.

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT CUSTOMER-FILE
               ASSIGN TO CUSTFILE
               ORGANIZATION IS INDEXED
               ACCESS MODE IS RANDOM
               RECORD KEY IS CUST-ID
               FILE STATUS IS WS-CUST-STATUS.

           SELECT REPORT-FILE
               ASSIGN TO RPTFILE
               ORGANIZATION IS SEQUENTIAL
               FILE STATUS IS WS-RPT-STATUS.

       DATA DIVISION.
       FILE SECTION.
       FD  CUSTOMER-FILE.
       01  CUSTOMER-RECORD.
           05  CUST-ID            PIC X(10).
           05  CUST-NAME          PIC X(30).
           05  CUST-BALANCE       PIC S9(7)V99 COMP-3.

       FD  REPORT-FILE.
       01  REPORT-RECORD          PIC X(132).

       WORKING-STORAGE SECTION.
       01  WS-CUST-STATUS         PIC XX.
       01  WS-RPT-STATUS          PIC XX.

       01  WS-ERROR-FLAGS.
           05  WS-CUST-ERROR-FLAG PIC 9 VALUE 0.
               88  WS-CUST-OK        VALUE 0.
               88  WS-CUST-ERROR     VALUE 1.
           05  WS-RPT-ERROR-FLAG  PIC 9 VALUE 0.
               88  WS-RPT-OK         VALUE 0.
               88  WS-RPT-ERROR      VALUE 1.

       01  WS-ERROR-MESSAGE       PIC X(80).

       PROCEDURE DIVISION.
       DECLARATIVES.

       CUST-FILE-ERROR SECTION.
           USE AFTER STANDARD ERROR PROCEDURE
               ON CUSTOMER-FILE.
       CUST-FILE-ERROR-HANDLER.
           STRING 'CUSTOMER FILE ERROR - STATUS: '
                  WS-CUST-STATUS
                  ' DURING I/O OPERATION'
               DELIMITED BY SIZE
               INTO WS-ERROR-MESSAGE
           END-STRING
           DISPLAY WS-ERROR-MESSAGE
           SET WS-CUST-ERROR TO TRUE
           .

       RPT-FILE-ERROR SECTION.
           USE AFTER STANDARD ERROR PROCEDURE
               ON REPORT-FILE.
       RPT-FILE-ERROR-HANDLER.
           STRING 'REPORT FILE ERROR - STATUS: '
                  WS-RPT-STATUS
               DELIMITED BY SIZE
               INTO WS-ERROR-MESSAGE
           END-STRING
           DISPLAY WS-ERROR-MESSAGE
           SET WS-RPT-ERROR TO TRUE
           .

       END DECLARATIVES.

       MAIN-SECTION SECTION.
       0000-MAIN.
           PERFORM 1000-OPEN-FILES
           IF WS-CUST-OK AND WS-RPT-OK
               PERFORM 2000-PROCESS-RECORDS
           END-IF
           PERFORM 3000-CLOSE-FILES
           STOP RUN
           .

16.2 File Status Codes: Complete Reference

File status codes are the primary mechanism for detecting and diagnosing I/O errors in COBOL. Every production COBOL program should define a FILE STATUS variable for every file it uses. After every I/O operation (OPEN, READ, WRITE, REWRITE, DELETE, START, CLOSE), the runtime places a two-character code in this variable that indicates the outcome of the operation.

Defining File Status

       FILE-CONTROL.
           SELECT MASTER-FILE
               ASSIGN TO MSTFILE
               ORGANIZATION IS INDEXED
               ACCESS MODE IS DYNAMIC
               RECORD KEY IS MST-KEY
               FILE STATUS IS WS-MST-STATUS.

       WORKING-STORAGE SECTION.
       01  WS-MST-STATUS          PIC XX.
           88  WS-MST-SUCCESS     VALUE '00'.
           88  WS-MST-EOF         VALUE '10'.
           88  WS-MST-DUPKEY      VALUE '22'.
           88  WS-MST-NOT-FOUND   VALUE '23'.

Complete File Status Code Reference

The file status code consists of two characters. The first character indicates the general category (status key 1), and the second character provides specific information within that category (status key 2).

Status Key 1 = '0': Successful Completion

Code Meaning
00 Operation completed successfully. No error, no exceptional condition.
02 Operation successful, but a duplicate key was detected. For READ, the record read has a duplicate alternate key. For WRITE or REWRITE, the record was written but created a duplicate alternate key value.
04 Operation successful, but the length of the record read does not match the fixed-length record description. (Record length mismatch.)
05 Operation successful. The OPEN refers to an optional file that is not present. The file will be created if it is opened for output.
07 Operation successful. For CLOSE with NO REWIND, REEL/UNIT, or FOR REMOVAL, the file is not on a reel/unit medium. For OPEN, the file is not on a reel/unit medium and the OPEN mode does not conflict with the file position.

Status Key 1 = '1': AT END Condition

Code Meaning
10 End of file reached. A sequential READ was attempted and no next logical record existed. This is the normal end-of-file condition.
14 A sequential READ was attempted on a relative file and the number of significant digits in the relative record number is larger than the size of the relative key data item.

Status Key 1 = '2': INVALID KEY Condition

Code Meaning
21 Sequence error. A sequential WRITE attempted to write a record with a key value that is not in ascending order, or the key was changed between a successful READ and the subsequent REWRITE.
22 Duplicate key. An attempt was made to WRITE or REWRITE a record that would create a duplicate value in a key field that does not allow duplicates.
23 Record not found. A READ, START, or DELETE attempted to access a record identified by a key, and that key value does not exist in the file.
24 Boundary violation. A WRITE went beyond the externally defined boundaries of the file. For an indexed file, this may mean the file is full. For a relative file, an attempt to WRITE beyond the limits of the file.

Status Key 1 = '3': Permanent Error

Code Meaning
30 Permanent I/O error. A hardware-level failure occurred that cannot be further classified. The operating system was unable to complete the I/O operation.
34 Boundary violation. A sequential WRITE was attempted and the file could not be extended because the disk was full or the maximum file size was reached.
35 An OPEN was attempted on a non-optional file that does not exist. The file is required but not present.
37 An OPEN was attempted on a file that does not support the specified open mode. For example, attempting to OPEN OUTPUT on a file that only supports input.
38 An OPEN was attempted on a file previously closed WITH LOCK.
39 A conflict was detected between the file attributes specified in the program and the actual file attributes. For example, the RECORD CONTAINS clause specifies a different length than the actual file records.

Status Key 1 = '4': Logic Error

Code Meaning
41 An OPEN was attempted on a file that is already open.
42 A CLOSE was attempted on a file that is not open.
43 For a sequential access file, the last I/O operation was not a successful READ before a REWRITE or DELETE was attempted.
44 A boundary violation occurred. A WRITE or REWRITE attempted to store a record that is larger than the maximum or smaller than the minimum record size allowed by the file description.
46 A sequential READ was attempted on a file opened for input or I-O, but no valid next record had been established because the previous READ was unsuccessful.
47 A READ or START was attempted on a file not opened for INPUT or I-O.
48 A WRITE was attempted on a file not opened for OUTPUT, I-O, or EXTEND.
49 A DELETE or REWRITE was attempted on a file not opened for I-O.

Status Key 1 = '9': Implementor-Defined

Code Meaning
90 Implementor-defined. On IBM z/OS, typically indicates a VSAM-specific error.
91 Implementor-defined. VSAM password failure (z/OS).
92 Implementor-defined. Logic error (IBM).
93 Implementor-defined. VSAM resource not available.
96 Implementor-defined. Missing DD statement (z/OS).
97 Implementor-defined. File integrity verification failed.

Checking File Status in Practice

The most robust approach to file status checking uses a centralized routine:

       WORKING-STORAGE SECTION.
       01  WS-FILE-STATUS         PIC XX.
       01  WS-FILE-NAME           PIC X(30).
       01  WS-IO-OPERATION        PIC X(10).
       01  WS-EXPECTED-STATUS     PIC XX.

       01  WS-FILE-ERROR-FLAG     PIC 9 VALUE 0.
           88  WS-FILE-OK         VALUE 0.
           88  WS-FILE-ERROR      VALUE 1.

       PROCEDURE DIVISION.

       9000-CHECK-FILE-STATUS.
           IF WS-FILE-STATUS NOT = WS-EXPECTED-STATUS
               DISPLAY '*** FILE I/O ERROR ***'
               DISPLAY 'FILE NAME:  ' WS-FILE-NAME
               DISPLAY 'OPERATION:  ' WS-IO-OPERATION
               DISPLAY 'STATUS:     ' WS-FILE-STATUS
               DISPLAY 'EXPECTED:   ' WS-EXPECTED-STATUS
               SET WS-FILE-ERROR TO TRUE
           END-IF
           .

       1100-OPEN-INPUT-FILE.
           OPEN INPUT CUSTOMER-FILE
           MOVE WS-CUST-STATUS   TO WS-FILE-STATUS
           MOVE 'CUSTOMER-FILE'  TO WS-FILE-NAME
           MOVE 'OPEN INPUT'     TO WS-IO-OPERATION
           MOVE '00'             TO WS-EXPECTED-STATUS
           PERFORM 9000-CHECK-FILE-STATUS
           .

       2100-READ-CUSTOMER.
           READ CUSTOMER-FILE
               INTO WS-CUSTOMER-RECORD
           END-READ
           MOVE WS-CUST-STATUS   TO WS-FILE-STATUS
           MOVE 'CUSTOMER-FILE'  TO WS-FILE-NAME
           MOVE 'READ'           TO WS-IO-OPERATION
           EVALUATE TRUE
               WHEN WS-CUST-STATUS = '00'
                   CONTINUE
               WHEN WS-CUST-STATUS = '10'
                   SET WS-END-OF-FILE TO TRUE
               WHEN OTHER
                   PERFORM 9000-CHECK-FILE-STATUS
           END-EVALUATE
           .

16.3 Inline Exception Handling: ON SIZE ERROR

The ON SIZE ERROR phrase is used with arithmetic statements (ADD, SUBTRACT, MULTIPLY, DIVIDE, COMPUTE) to detect situations where the result of a calculation is too large to fit in the receiving field, or when a division by zero occurs.

Without ON SIZE ERROR

Without ON SIZE ERROR, an arithmetic overflow is silently truncated. The result is wrong, and the program continues as if nothing happened:

       01  WS-AMOUNT          PIC 9(3) VALUE 999.
       01  WS-INCREMENT       PIC 9(3) VALUE 5.

           ADD WS-INCREMENT TO WS-AMOUNT
      *    WS-AMOUNT now contains 004 (1004 truncated to 3 digits)
      *    No error is raised -- the program continues with bad data

This silent truncation is one of the most dangerous behaviors in COBOL. In financial applications, it can cause millions of dollars in discrepancies that are extremely difficult to trace.

With ON SIZE ERROR

           ADD WS-INCREMENT TO WS-AMOUNT
               ON SIZE ERROR
                   DISPLAY 'ARITHMETIC OVERFLOW IN ADD'
                   DISPLAY 'AMOUNT: ' WS-AMOUNT
                   DISPLAY 'INCREMENT: ' WS-INCREMENT
                   MOVE 99 TO WS-RETURN-CODE
               NOT ON SIZE ERROR
                   ADD 1 TO WS-RECORDS-PROCESSED
           END-ADD

When ON SIZE ERROR is specified and an overflow occurs, the receiving field is not modified (it retains its previous value) and the imperative statements following ON SIZE ERROR are executed. When no overflow occurs, the NOT ON SIZE ERROR statements execute.

Division by Zero

The ON SIZE ERROR phrase is the only safe way to handle division by zero in COBOL:

           DIVIDE WS-TOTAL-AMOUNT BY WS-NUM-TRANSACTIONS
               GIVING WS-AVERAGE-AMOUNT
               REMAINDER WS-REMAINDER
               ON SIZE ERROR
                   DISPLAY 'DIVISION BY ZERO OR OVERFLOW'
                   MOVE ZEROS TO WS-AVERAGE-AMOUNT
               NOT ON SIZE ERROR
                   CONTINUE
           END-DIVIDE

COMPUTE with ON SIZE ERROR

The COMPUTE statement is particularly important to protect because it can involve complex expressions where overflow is harder to predict:

           COMPUTE WS-COMPOUND-INTEREST =
               WS-PRINCIPAL *
               ((1 + WS-RATE / 100) ** WS-YEARS - 1)
               ON SIZE ERROR
                   DISPLAY 'INTEREST CALCULATION OVERFLOW'
                   DISPLAY 'PRINCIPAL: ' WS-PRINCIPAL
                   DISPLAY 'RATE:      ' WS-RATE
                   DISPLAY 'YEARS:     ' WS-YEARS
                   MOVE HIGH-VALUES TO WS-COMPOUND-INTEREST
               NOT ON SIZE ERROR
                   CONTINUE
           END-COMPUTE

16.4 ON OVERFLOW for String Operations

The ON OVERFLOW phrase applies to the STRING and UNSTRING statements. It triggers when the destination field is too small to hold the concatenated result (STRING) or when there are more source segments than receiving fields (UNSTRING).

STRING with ON OVERFLOW

       01  WS-FULL-NAME       PIC X(40).
       01  WS-POINTER         PIC 99 VALUE 1.

           STRING
               WS-FIRST-NAME DELIMITED BY SPACES
               ' '           DELIMITED BY SIZE
               WS-MIDDLE-INIT DELIMITED BY SPACES
               '. '          DELIMITED BY SIZE
               WS-LAST-NAME  DELIMITED BY SPACES
               INTO WS-FULL-NAME
               WITH POINTER WS-POINTER
               ON OVERFLOW
                   DISPLAY 'NAME TRUNCATED: EXCEEDS 40 CHARS'
                   MOVE 'Y' TO WS-NAME-TRUNCATED
               NOT ON OVERFLOW
                   MOVE 'N' TO WS-NAME-TRUNCATED
           END-STRING

UNSTRING with ON OVERFLOW

       01  WS-INPUT-LINE      PIC X(80).
       01  WS-FIELD-1         PIC X(20).
       01  WS-FIELD-2         PIC X(20).
       01  WS-FIELD-3         PIC X(20).
       01  WS-FIELD-COUNT     PIC 99.

           UNSTRING WS-INPUT-LINE
               DELIMITED BY ',' OR SPACES
               INTO WS-FIELD-1
                    WS-FIELD-2
                    WS-FIELD-3
               TALLYING IN WS-FIELD-COUNT
               ON OVERFLOW
                   DISPLAY 'MORE FIELDS THAN EXPECTED: '
                       WS-FIELD-COUNT
               NOT ON OVERFLOW
                   CONTINUE
           END-UNSTRING

16.5 INVALID KEY for File Operations

The INVALID KEY phrase applies to random-access file operations on indexed and relative files: READ (with KEY IS), WRITE, REWRITE, DELETE, and START. It triggers when the operation fails because the specified key value does not exist, already exists (for a unique key write), or is out of sequence.

READ with INVALID KEY

           MOVE WS-SEARCH-KEY TO CUST-KEY

           READ CUSTOMER-FILE
               INTO WS-CUSTOMER-RECORD
               KEY IS CUST-KEY
               INVALID KEY
                   DISPLAY 'CUSTOMER NOT FOUND: ' WS-SEARCH-KEY
                   SET WS-CUSTOMER-NOT-FOUND TO TRUE
               NOT INVALID KEY
                   SET WS-CUSTOMER-FOUND TO TRUE
                   ADD 1 TO WS-RECORDS-READ
           END-READ

WRITE with INVALID KEY

           WRITE CUSTOMER-RECORD
               FROM WS-NEW-CUSTOMER
               INVALID KEY
                   EVALUATE WS-CUST-STATUS
                       WHEN '22'
                           DISPLAY 'DUPLICATE KEY: '
                               WS-NEW-CUSTOMER-KEY
                       WHEN '24'
                           DISPLAY 'FILE IS FULL'
                       WHEN OTHER
                           DISPLAY 'WRITE ERROR, STATUS: '
                               WS-CUST-STATUS
                   END-EVALUATE
               NOT INVALID KEY
                   ADD 1 TO WS-RECORDS-WRITTEN
           END-WRITE

DELETE with INVALID KEY

           MOVE WS-DELETE-KEY TO CUST-KEY

           DELETE CUSTOMER-FILE RECORD
               INVALID KEY
                   DISPLAY 'CANNOT DELETE - NOT FOUND: '
                       WS-DELETE-KEY
                   ADD 1 TO WS-DELETE-ERRORS
               NOT INVALID KEY
                   ADD 1 TO WS-RECORDS-DELETED
           END-DELETE

START with INVALID KEY

           MOVE WS-START-KEY TO CUST-KEY

           START CUSTOMER-FILE
               KEY IS >= CUST-KEY
               INVALID KEY
                   DISPLAY 'NO RECORDS >= ' WS-START-KEY
                   SET WS-NO-RECORDS TO TRUE
               NOT INVALID KEY
                   SET WS-RECORDS-EXIST TO TRUE
           END-START

16.6 AT END for Sequential Reads

The AT END phrase is used with the sequential READ statement to detect the end-of-file condition (file status '10'). This is the most commonly used inline exception phrase in COBOL, appearing in virtually every program that reads a sequential file.

Basic AT END Pattern

       01  WS-EOF-FLAG        PIC 9 VALUE 0.
           88  WS-NOT-EOF     VALUE 0.
           88  WS-EOF         VALUE 1.

       2000-PROCESS-FILE.
           PERFORM 2100-READ-INPUT
           PERFORM UNTIL WS-EOF
               PERFORM 2200-PROCESS-RECORD
               PERFORM 2100-READ-INPUT
           END-PERFORM
           .

       2100-READ-INPUT.
           READ INPUT-FILE
               INTO WS-INPUT-RECORD
               AT END
                   SET WS-EOF TO TRUE
               NOT AT END
                   ADD 1 TO WS-RECORDS-READ
           END-READ
           .

RETURN with AT END

When using INPUT PROCEDURE or OUTPUT PROCEDURE with a SORT statement, the RETURN statement replaces READ, and it also supports AT END:

       OUTPUT-PROCEDURE-SECTION SECTION.
           PERFORM UNTIL WS-EOF
               RETURN SORT-FILE
                   INTO WS-SORTED-RECORD
                   AT END
                       SET WS-EOF TO TRUE
                   NOT AT END
                       PERFORM WRITE-SORTED-RECORD
               END-RETURN
           END-PERFORM
           .

16.7 COBOL 2002 RAISE and RESUME: Structured Exception Handling

The COBOL 2002 standard introduced a structured exception handling mechanism modeled on the try/catch paradigm found in modern programming languages. This feature uses three new statements: RAISE, RESUME, and an extended form of the DECLARATIVES section using USE AFTER EXCEPTION.

Note on Availability

The RAISE/RESUME mechanism is part of the ISO 2002 COBOL standard and is supported by GnuCOBOL (version 3.0 and later with appropriate configuration) and Micro Focus COBOL. IBM Enterprise COBOL as of version 6.4 does not support RAISE/RESUME; IBM instead relies on Language Environment condition handling, which is covered in Section 16.8. If you are working on IBM mainframes, you should be aware of the COBOL 2002 mechanism for portability and future reference, but you will use LE condition handling in practice.

Defining Exception Names

Exception names are defined in the SPECIAL-NAMES paragraph of the ENVIRONMENT DIVISION:

       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       REPOSITORY.
           FUNCTION ALL INTRINSIC.

       SPECIAL-NAMES.
           EC-SIZE
           EC-SIZE-ZERO-DIVIDE
           EC-DATA
           EC-DATA-INCOMPATIBLE
           EC-FLOW
           EC-FLOW-GLOBAL-GOBACK
           .

The COBOL 2002 standard defines a hierarchy of exception classes. The top-level classes include EC-SIZE (arithmetic errors), EC-DATA (data conversion errors), EC-FLOW (control flow errors), EC-I-O (input/output errors), EC-RANGE (subscript and reference modification range errors), and others.

The RAISE Statement

The RAISE statement explicitly signals an exception condition:

      * Raise a predefined exception
       RAISE EC-SIZE-ZERO-DIVIDE

      * Raise a user-defined exception object
       RAISE EXCEPTION WS-MY-EXCEPTION

Exception Handling with USE AFTER EXCEPTION

Declarative procedures can catch raised exceptions:

       PROCEDURE DIVISION.
       DECLARATIVES.

       SIZE-ERROR SECTION.
           USE AFTER EXCEPTION CONDITION EC-SIZE.
       SIZE-ERROR-HANDLER.
           DISPLAY 'SIZE ERROR CAUGHT'
           DISPLAY 'EXCEPTION: '
               FUNCTION EXCEPTION-STATUS
           RESUME AT NEXT STATEMENT
           .

       END DECLARATIVES.

       MAIN-SECTION SECTION.
       0000-MAIN.
           COMPUTE WS-RESULT = WS-A / WS-B
      *    If WS-B is zero, control transfers to
      *    SIZE-ERROR-HANDLER, then RESUME AT NEXT
      *    STATEMENT brings control here:
           DISPLAY 'AFTER COMPUTE'
           STOP RUN
           .

The RESUME Statement

The RESUME statement is used within a declarative exception handler to specify where execution should continue after the exception has been handled:

      * Continue with the next statement after the one that
      * raised the exception
       RESUME AT NEXT STATEMENT

      * Transfer control to a specific paragraph or section
       RESUME AT 9000-ERROR-EXIT

TURN Directive for Exception Checking

The TURN directive controls which exception conditions are actively checked:

      >>TURN EC-SIZE CHECKING ON
      >>TURN EC-DATA CHECKING ON
      >>TURN EC-RANGE CHECKING ON

      * These exceptions are now actively monitored
      * Performance cost: runtime checks are inserted

      >>TURN EC-SIZE CHECKING OFF
      * Size checks are no longer performed

Practical Example with RAISE/RESUME

       IDENTIFICATION DIVISION.
       PROGRAM-ID. RAISEDEM.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-NUMERATOR        PIC 9(5)  VALUE 100.
       01  WS-DENOMINATOR      PIC 9(5)  VALUE 0.
       01  WS-RESULT           PIC 9(5)V99.
       01  WS-ERROR-COUNT      PIC 9(3)  VALUE 0.

       PROCEDURE DIVISION.
       DECLARATIVES.
       ARITH-ERROR SECTION.
           USE AFTER EXCEPTION CONDITION EC-SIZE.
       ARITH-ERROR-PARA.
           ADD 1 TO WS-ERROR-COUNT
           DISPLAY 'ARITHMETIC EXCEPTION HANDLED'
           DISPLAY 'ERROR COUNT: ' WS-ERROR-COUNT
           MOVE 0 TO WS-RESULT
           RESUME AT NEXT STATEMENT
           .
       END DECLARATIVES.

       MAIN-SECTION SECTION.
       MAIN-PARA.
           DIVIDE WS-NUMERATOR BY WS-DENOMINATOR
               GIVING WS-RESULT
           END-DIVIDE
           DISPLAY 'RESULT: ' WS-RESULT
           DISPLAY 'PROGRAM CONTINUES NORMALLY'
           STOP RUN
           .

16.8 IBM Language Environment Condition Handling

On IBM z/OS mainframes, COBOL programs run within the Language Environment (LE), a common runtime environment shared by COBOL, PL/I, C, and Assembler programs. LE provides a sophisticated condition handling model that intercepts errors at a level below the COBOL language, handling hardware interrupts (program checks), software-detected errors, and user-defined conditions.

The LE Condition Handling Model

LE classifies conditions by severity:

Severity Meaning Default Action
0 Information Continue
1 Warning Continue
2 Error Continue with default correction
3 Severe error Terminate the enclave
4 Critical error Terminate the enclave

Condition Tokens

Every LE condition is identified by a 12-byte condition token stored in a feedback code structure. The condition token contains a condition ID, a facility ID (identifying the component that detected the condition), and severity information.

The CEECBLCK Copybook

IBM provides the CEECBLCK copybook that defines the LE condition handling data structures:

       WORKING-STORAGE SECTION.
       COPY CEECBLCK.

      * The copybook defines:
      * 01 FC.              (Feedback Code)
      *    05 Condition-ID.
      *       10 Case-1-Condition-ID.
      *          15 Severity    PIC S9(4) COMP.
      *          15 Msg-No      PIC S9(4) COMP.
      *       10 Case-2-Condition-ID
      *          REDEFINES Case-1-Condition-ID.
      *          15 Class-Code  PIC S9(4) COMP.
      *          15 Cause-Code  PIC S9(4) COMP.
      *    05 Facility-ID     PIC X(3).
      *    05 I-S-Info        PIC S9(9) COMP.

Using CEE3SRP to Set Resume Points

The CEE3SRP service establishes a resume point. If a condition occurs that would normally terminate the program, LE can instead resume execution at the resume point:

       WORKING-STORAGE SECTION.
       COPY CEECBLCK.
       01  WS-RESUME-TOKEN     PIC S9(9) COMP VALUE 0.

       PROCEDURE DIVISION.

       1000-MAIN.
           CALL 'CEE3SRP'
               USING WS-RESUME-TOKEN
                     FC
           END-CALL

           EVALUATE TRUE
               WHEN WS-RESUME-TOKEN = 0
      *            Normal flow -- first time through
                   PERFORM 2000-RISKY-OPERATION
               WHEN WS-RESUME-TOKEN = 1
      *            We got here via resume after an error
                   DISPLAY 'RECOVERED FROM ERROR'
                   PERFORM 9000-ERROR-RECOVERY
           END-EVALUATE
           .

CEECBLCK Return Code Checking

A common pattern uses LE callable services and checks the feedback code:

       WORKING-STORAGE SECTION.
       COPY CEECBLCK.
       01  WS-DATE-INT         PIC S9(9) COMP.
       01  WS-INPUT-DATE       PIC X(8) VALUE '20250230'.
       01  WS-DATE-PICTURE     PIC X(8) VALUE 'YYYYMMDD'.

       PROCEDURE DIVISION.
       1000-VALIDATE-DATE.
           CALL 'CEEDAYS'
               USING WS-INPUT-DATE
                     WS-DATE-PICTURE
                     WS-DATE-INT
                     FC
           END-CALL

           IF CEE000 OF FC
               DISPLAY 'DATE IS VALID'
               DISPLAY 'LILLIAN DAY: ' WS-DATE-INT
           ELSE
               DISPLAY 'INVALID DATE: ' WS-INPUT-DATE
               DISPLAY 'SEVERITY: ' Severity OF FC
               DISPLAY 'MSG-NO:   ' Msg-No OF FC
           END-IF
           .

16.9 Return Codes and Program Communication

Return codes are the standard mechanism for communicating success or failure between COBOL programs and between COBOL programs and the operating system.

The RETURN-CODE Special Register

IBM Enterprise COBOL provides the RETURN-CODE special register, a fullword binary (PIC S9(4) COMP) field that is automatically available without declaration:

       PROCEDURE DIVISION.
       0000-MAIN.
           PERFORM 1000-PROCESS
           IF WS-ERROR-OCCURRED
               MOVE 16 TO RETURN-CODE
           ELSE
               MOVE 0  TO RETURN-CODE
           END-IF
           STOP RUN
           .

Standard Return Code Conventions

On z/OS mainframes, return codes follow a well-established convention:

Return Code Meaning
0 Successful completion
4 Warning -- processing completed but with minor issues
8 Error -- some processing could not be completed
12 Severe error -- significant processing failure
16 Terminal error -- program cannot continue
20 Catastrophic error -- system-level failure

Checking Return Codes from Called Programs

       01  WS-CALLED-RC       PIC S9(4) COMP.

       2000-CALL-SUBPROGRAM.
           CALL 'VALIDATECUST'
               USING WS-CUSTOMER-DATA
           END-CALL
           MOVE RETURN-CODE TO WS-CALLED-RC
           EVALUATE WS-CALLED-RC
               WHEN 0
                   PERFORM 2100-PROCESS-VALID-CUST
               WHEN 4
                   DISPLAY 'WARNING FROM VALIDATION'
                   PERFORM 2100-PROCESS-VALID-CUST
               WHEN 8
                   DISPLAY 'VALIDATION ERRORS FOUND'
                   PERFORM 2900-LOG-ERRORS
               WHEN OTHER
                   DISPLAY 'SEVERE VALIDATION FAILURE, RC='
                       WS-CALLED-RC
                   PERFORM 9000-ABORT
           END-EVALUATE
           .

Setting Return Codes in Subprograms

       IDENTIFICATION DIVISION.
       PROGRAM-ID. VALIDATECUST.

       DATA DIVISION.
       LINKAGE SECTION.
       01  LS-CUSTOMER-DATA.
           05  LS-CUST-ID     PIC X(10).
           05  LS-CUST-NAME   PIC X(30).
           05  LS-CUST-BAL    PIC S9(7)V99 COMP-3.

       PROCEDURE DIVISION USING LS-CUSTOMER-DATA.
       0000-VALIDATE.
           MOVE 0 TO RETURN-CODE

           IF LS-CUST-ID = SPACES
               DISPLAY 'CUSTOMER ID IS BLANK'
               MOVE 8 TO RETURN-CODE
           END-IF

           IF LS-CUST-NAME = SPACES
               DISPLAY 'CUSTOMER NAME IS BLANK'
               MOVE 8 TO RETURN-CODE
           END-IF

           IF LS-CUST-BAL < 0
               DISPLAY 'WARNING: NEGATIVE BALANCE'
               IF RETURN-CODE < 4
                   MOVE 4 TO RETURN-CODE
               END-IF
           END-IF

           GOBACK
           .

16.10 Common Abend Codes

An abend (abnormal end) occurs when a program terminates unexpectedly due to an unrecoverable error. On z/OS, abends are classified as system abends (S-type, issued by the operating system or subsystem) and user abends (U-type, issued by the program or runtime). Understanding common abend codes is essential for debugging production failures.

S0C7: Data Exception

The S0C7 is the most common COBOL abend. It occurs when a packed decimal (COMP-3) or zoned decimal field contains invalid data -- characters that are not valid digits in the expected format.

Common causes:

  • An uninitialized WORKING-STORAGE variable used in arithmetic
  • A file record containing corrupt data
  • A MOVE of alphanumeric data into a numeric field without validation
  • A REDEFINES that overlays a numeric field with incompatible data
  • Reading past the end of a file without checking file status

Example that causes S0C7:

       01  WS-AMOUNT       PIC S9(5)V99 COMP-3.
       01  WS-INPUT        PIC X(10) VALUE 'ABC'.

      * This will abend S0C7 when WS-AMOUNT is used in arithmetic:
           MOVE WS-INPUT TO WS-AMOUNT
           ADD WS-AMOUNT TO WS-TOTAL

Prevention:

       01  WS-AMOUNT       PIC S9(5)V99 COMP-3.
       01  WS-INPUT        PIC X(10).

           IF WS-INPUT IS NUMERIC
               MOVE WS-INPUT TO WS-AMOUNT
               ADD WS-AMOUNT TO WS-TOTAL
           ELSE
               DISPLAY 'INVALID NUMERIC DATA: ' WS-INPUT
               ADD 1 TO WS-ERROR-COUNT
           END-IF

S0C4: Protection Exception

The S0C4 occurs when a program attempts to access memory that it does not own or that is not allocated. This is the COBOL equivalent of a segmentation fault.

Common causes:

  • Subscript or index out of range (table overflow)
  • Invalid address in a pointer or LINKAGE SECTION reference
  • Called program not properly linked or not found (in some scenarios)
  • CALL with a mismatched USING parameter list
  • Reference modification with out-of-range offset or length
  • Using a LINKAGE SECTION item when no data was passed

Example that causes S0C4:

       01  WS-TABLE.
           05  WS-ELEMENT  PIC X(10) OCCURS 100 TIMES.
       01  WS-INDEX        PIC 9(4).

      * If WS-INDEX = 101 or greater, S0C4:
           MOVE 'DATA' TO WS-ELEMENT(WS-INDEX)

Prevention:

           IF WS-INDEX > 0 AND WS-INDEX <= 100
               MOVE 'DATA' TO WS-ELEMENT(WS-INDEX)
           ELSE
               DISPLAY 'INDEX OUT OF RANGE: ' WS-INDEX
               ADD 1 TO WS-ERROR-COUNT
           END-IF

S0C1: Operation Exception

The S0C1 occurs when the CPU encounters an invalid instruction. In COBOL, this typically means control has jumped to an area of memory that does not contain valid instructions.

Common causes:

  • A CALL to a program name that resolves to an invalid address
  • A corrupted program load module
  • Falling through the end of a program without a STOP RUN or GOBACK
  • An unresolved external reference at link-edit time

Prevention:

      * Always use ON EXCEPTION with dynamic calls
           CALL WS-PROGRAM-NAME
               USING WS-PARAMETERS
               ON EXCEPTION
                   DISPLAY 'PROGRAM NOT FOUND: '
                       WS-PROGRAM-NAME
                   MOVE 16 TO RETURN-CODE
           END-CALL

      * Always end programs with GOBACK or STOP RUN
       0000-MAIN.
           PERFORM 1000-PROCESS
           GOBACK
           .

S0CB: Division by Zero

The S0CB occurs when a fixed-point division by zero is attempted. While ON SIZE ERROR can prevent this for COBOL arithmetic statements, raw COMP arithmetic or arithmetic in called routines may still produce this abend.

Prevention:

           IF WS-DIVISOR NOT = ZERO
               DIVIDE WS-DIVIDEND BY WS-DIVISOR
                   GIVING WS-QUOTIENT
               END-DIVIDE
           ELSE
               DISPLAY 'DIVISION BY ZERO AVOIDED'
               MOVE ZEROS TO WS-QUOTIENT
           END-IF

S322: Time Limit Exceeded

The S322 abend occurs when a job step exceeds its CPU time limit as specified by the TIME parameter on the JOB or EXEC JCL statement.

Common causes:

  • An infinite loop in the program
  • Processing a much larger file than expected
  • A PERFORM UNTIL with a condition that is never satisfied
  • Inefficient I/O patterns (reading an entire VSAM file randomly when sequential would suffice)

Prevention:

       01  WS-LOOP-COUNTER    PIC 9(9) COMP VALUE 0.
       01  WS-MAX-ITERATIONS  PIC 9(9) COMP VALUE 10000000.

       2000-PROCESS-LOOP.
           PERFORM UNTIL WS-DONE OR
                         WS-LOOP-COUNTER >= WS-MAX-ITERATIONS
               ADD 1 TO WS-LOOP-COUNTER
               PERFORM 2100-PROCESS-RECORD
               PERFORM 2200-READ-NEXT
           END-PERFORM

           IF WS-LOOP-COUNTER >= WS-MAX-ITERATIONS
               DISPLAY 'SAFETY LIMIT REACHED: POSSIBLE LOOP'
               MOVE 16 TO RETURN-CODE
           END-IF
           .

S806: Module Not Found

The S806 abend occurs when a dynamically called program (CALL identifier) cannot be found in the load library concatenation.

Prevention:

           CALL WS-PROGRAM-NAME
               USING WS-PARAMETERS
               ON EXCEPTION
                   DISPLAY 'S806 PREVENTED - MODULE NOT FOUND: '
                       WS-PROGRAM-NAME
                   MOVE 16 TO RETURN-CODE
               NOT ON EXCEPTION
                   MOVE RETURN-CODE TO WS-SUB-RC
           END-CALL

16.11 Error Logging Patterns

Production COBOL programs need comprehensive error logging to support problem diagnosis. Simply displaying an error message is not sufficient; production error logs need structured information that operations and support staff can use to identify, classify, and resolve problems quickly.

Structured Error Log Record

       01  WS-ERROR-LOG-RECORD.
           05  EL-TIMESTAMP.
               10  EL-DATE         PIC X(10).
               10  FILLER          PIC X VALUE ' '.
               10  EL-TIME         PIC X(08).
           05  FILLER              PIC X VALUE ' '.
           05  EL-PROGRAM-ID       PIC X(08).
           05  FILLER              PIC X VALUE ' '.
           05  EL-SEVERITY         PIC X(01).
               88  EL-INFO         VALUE 'I'.
               88  EL-WARNING      VALUE 'W'.
               88  EL-ERROR        VALUE 'E'.
               88  EL-SEVERE       VALUE 'S'.
           05  FILLER              PIC X VALUE ' '.
           05  EL-ERROR-CODE       PIC X(08).
           05  FILLER              PIC X VALUE ' '.
           05  EL-PARAGRAPH        PIC X(30).
           05  FILLER              PIC X VALUE ' '.
           05  EL-MESSAGE          PIC X(80).
           05  FILLER              PIC X VALUE ' '.
           05  EL-DATA-1           PIC X(30).
           05  FILLER              PIC X VALUE ' '.
           05  EL-DATA-2           PIC X(30).

Error Logging Routine

       9100-LOG-ERROR.
           MOVE FUNCTION CURRENT-DATE(1:4) TO EL-DATE(1:4)
           MOVE '-'                        TO EL-DATE(5:1)
           MOVE FUNCTION CURRENT-DATE(5:2) TO EL-DATE(6:2)
           MOVE '-'                        TO EL-DATE(8:1)
           MOVE FUNCTION CURRENT-DATE(7:2) TO EL-DATE(9:2)

           MOVE FUNCTION CURRENT-DATE(9:2)  TO EL-TIME(1:2)
           MOVE ':'                          TO EL-TIME(3:1)
           MOVE FUNCTION CURRENT-DATE(11:2)  TO EL-TIME(4:2)
           MOVE ':'                          TO EL-TIME(6:1)
           MOVE FUNCTION CURRENT-DATE(13:2)  TO EL-TIME(7:2)

           MOVE 'CUSTPROC' TO EL-PROGRAM-ID

           WRITE ERROR-LOG-RECORD
               FROM WS-ERROR-LOG-RECORD
           END-WRITE

           ADD 1 TO WS-ERROR-COUNT

           INITIALIZE WS-ERROR-LOG-RECORD
           .

       9200-LOG-FILE-ERROR.
           SET EL-ERROR TO TRUE
           MOVE WS-FILE-STATUS     TO EL-ERROR-CODE
           MOVE WS-CURRENT-PARA    TO EL-PARAGRAPH
           MOVE WS-FILE-ERROR-MSG  TO EL-MESSAGE
           MOVE WS-FILE-NAME       TO EL-DATA-1
           MOVE WS-IO-OPERATION    TO EL-DATA-2
           PERFORM 9100-LOG-ERROR
           .

Error Summary Report

At the end of processing, a summary of all errors should be produced:

       3000-PRODUCE-SUMMARY.
           DISPLAY '========================================='
           DISPLAY 'PROCESSING SUMMARY'
           DISPLAY '========================================='
           DISPLAY 'RECORDS READ:       ' WS-RECORDS-READ
           DISPLAY 'RECORDS PROCESSED:  ' WS-RECORDS-PROCESSED
           DISPLAY 'RECORDS WRITTEN:    ' WS-RECORDS-WRITTEN
           DISPLAY 'RECORDS IN ERROR:   ' WS-ERROR-COUNT
           DISPLAY '========================================='

           IF WS-ERROR-COUNT > 0
               DISPLAY '*** ERRORS OCCURRED - SEE ERROR LOG ***'
               MOVE 8 TO RETURN-CODE
           ELSE
               DISPLAY 'PROCESSING COMPLETED SUCCESSFULLY'
               MOVE 0 TO RETURN-CODE
           END-IF
           .

16.12 Defensive Programming Techniques

Defensive programming means writing code that anticipates and handles unexpected conditions before they become abends. In COBOL, where a single production failure can delay payroll processing or corrupt financial records, defensive programming is not optional -- it is a professional obligation.

Validate All Input Data

Never assume that data arriving from an external source (file, database, screen, or parameter) is valid:

       3000-VALIDATE-INPUT-RECORD.
           SET WS-RECORD-VALID TO TRUE

      *    Check required fields are not blank
           IF IR-CUSTOMER-ID = SPACES
               MOVE 'CUSTOMER ID IS BLANK'
                   TO WS-VALIDATION-MSG
               PERFORM 3900-RECORD-ERROR
           END-IF

      *    Check numeric fields contain valid numbers
           IF IR-AMOUNT NOT NUMERIC
               MOVE 'AMOUNT IS NOT NUMERIC'
                   TO WS-VALIDATION-MSG
               PERFORM 3900-RECORD-ERROR
           END-IF

      *    Check range validity
           IF IR-AMOUNT IS NUMERIC
               IF IR-AMOUNT > 999999.99
                   MOVE 'AMOUNT EXCEEDS MAXIMUM'
                       TO WS-VALIDATION-MSG
                   PERFORM 3900-RECORD-ERROR
               END-IF
           END-IF

      *    Check date validity
           IF IR-TRANS-DATE NOT NUMERIC
               MOVE 'TRANSACTION DATE NOT NUMERIC'
                   TO WS-VALIDATION-MSG
               PERFORM 3900-RECORD-ERROR
           ELSE
               PERFORM 3100-VALIDATE-DATE
           END-IF

      *    Check code fields against valid values
           EVALUATE IR-TRANS-TYPE
               WHEN 'C'
               WHEN 'D'
               WHEN 'T'
                   CONTINUE
               WHEN OTHER
                   STRING 'INVALID TRANS TYPE: '
                       IR-TRANS-TYPE
                       DELIMITED BY SIZE
                       INTO WS-VALIDATION-MSG
                   END-STRING
                   PERFORM 3900-RECORD-ERROR
           END-EVALUATE
           .

Initialize All Variables

Uninitialized variables are a leading cause of S0C7 abends:

       WORKING-STORAGE SECTION.
      * Always initialize numeric fields
       01  WS-TOTAL-AMOUNT     PIC S9(7)V99 COMP-3 VALUE 0.
       01  WS-RECORD-COUNT     PIC 9(7)     COMP-3 VALUE 0.
       01  WS-ERROR-COUNT      PIC 9(5)     COMP-3 VALUE 0.

      * Always initialize flags
       01  WS-EOF-FLAG         PIC 9        VALUE 0.
           88  WS-NOT-EOF      VALUE 0.
           88  WS-EOF          VALUE 1.

      * Use INITIALIZE for group items
       1000-INITIALIZE.
           INITIALIZE WS-COUNTERS
           INITIALIZE WS-ACCUMULATORS
           INITIALIZE WS-FLAGS
           .

Protect Array Boundaries

       01  WS-STATE-TABLE.
           05  WS-STATE-ENTRY OCCURS 50 TIMES
               INDEXED BY WS-STATE-IDX.
               10  WS-STATE-CODE   PIC XX.
               10  WS-STATE-NAME   PIC X(20).
               10  WS-STATE-TAX    PIC V99.
       01  WS-TABLE-SIZE       PIC 99 VALUE 50.

       4000-LOOKUP-STATE.
           SET WS-STATE-IDX TO 1
           SEARCH WS-STATE-ENTRY
               AT END
                   DISPLAY 'STATE NOT FOUND: '
                       WS-SEARCH-STATE
                   SET WS-STATE-NOT-FOUND TO TRUE
               WHEN WS-STATE-CODE(WS-STATE-IDX) =
                   WS-SEARCH-STATE
                   MOVE WS-STATE-NAME(WS-STATE-IDX)
                       TO WS-RESULT-NAME
                   MOVE WS-STATE-TAX(WS-STATE-IDX)
                       TO WS-RESULT-TAX
                   SET WS-STATE-FOUND TO TRUE
           END-SEARCH
           .

Always Check File Status After Every I/O

       9000-CHECK-STATUS.
           EVALUATE TRUE
               WHEN WS-FILE-STATUS = '00'
                   CONTINUE
               WHEN WS-FILE-STATUS = '10'
                   SET WS-EOF TO TRUE
               WHEN WS-FILE-STATUS = '23'
                   SET WS-RECORD-NOT-FOUND TO TRUE
               WHEN WS-FILE-STATUS = '02'
                   CONTINUE
               WHEN WS-FILE-STATUS(1:1) = '9'
                   DISPLAY 'VSAM ERROR: ' WS-FILE-STATUS
                   DISPLAY 'ON FILE: ' WS-CURRENT-FILE
                   MOVE 16 TO RETURN-CODE
                   PERFORM 9999-ABORT
               WHEN OTHER
                   DISPLAY 'UNEXPECTED STATUS: '
                       WS-FILE-STATUS
                   DISPLAY 'ON FILE: ' WS-CURRENT-FILE
                   MOVE 12 TO RETURN-CODE
                   PERFORM 9999-ABORT
           END-EVALUATE
           .

Guard Against Division by Zero

       5000-CALCULATE-AVERAGE.
           IF WS-COUNT > 0
               DIVIDE WS-TOTAL BY WS-COUNT
                   GIVING WS-AVERAGE ROUNDED
                   ON SIZE ERROR
                       DISPLAY 'AVERAGE CALCULATION OVERFLOW'
                       MOVE 0 TO WS-AVERAGE
               END-DIVIDE
           ELSE
               MOVE 0 TO WS-AVERAGE
           END-IF
           .

16.13 Production Error Handling Framework

A complete production error handling framework combines all the techniques discussed in this chapter into a cohesive structure. The following program demonstrates a complete framework that can be adapted for any batch COBOL application.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. PRODFRAME.
      *========================================================*
      * PROGRAM:  PRODFRAME                                     *
      * PURPOSE:  Production Error Handling Framework            *
      * AUTHOR:   Enterprise COBOL Team                         *
      *========================================================*

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT INPUT-FILE
               ASSIGN TO INFILE
               ORGANIZATION IS SEQUENTIAL
               FILE STATUS IS WS-INPUT-STATUS.

           SELECT OUTPUT-FILE
               ASSIGN TO OUTFILE
               ORGANIZATION IS SEQUENTIAL
               FILE STATUS IS WS-OUTPUT-STATUS.

           SELECT ERROR-FILE
               ASSIGN TO ERRFILE
               ORGANIZATION IS SEQUENTIAL
               FILE STATUS IS WS-ERROR-STATUS.

           SELECT ERROR-LOG
               ASSIGN TO ERRLOG
               ORGANIZATION IS SEQUENTIAL
               FILE STATUS IS WS-LOG-STATUS.

       DATA DIVISION.
       FILE SECTION.

       FD  INPUT-FILE
           RECORDING MODE IS F
           RECORD CONTAINS 80 CHARACTERS.
       01  INPUT-RECORD            PIC X(80).

       FD  OUTPUT-FILE
           RECORDING MODE IS F
           RECORD CONTAINS 80 CHARACTERS.
       01  OUTPUT-RECORD           PIC X(80).

       FD  ERROR-FILE
           RECORDING MODE IS F
           RECORD CONTAINS 80 CHARACTERS.
       01  ERROR-RECORD            PIC X(80).

       FD  ERROR-LOG
           RECORDING MODE IS F
           RECORD CONTAINS 200 CHARACTERS.
       01  LOG-RECORD              PIC X(200).

       WORKING-STORAGE SECTION.

       01  WS-FILE-STATUSES.
           05  WS-INPUT-STATUS     PIC XX.
           05  WS-OUTPUT-STATUS    PIC XX.
           05  WS-ERROR-STATUS     PIC XX.
           05  WS-LOG-STATUS       PIC XX.

       01  WS-CONTROL-FLAGS.
           05  WS-EOF-FLAG         PIC 9 VALUE 0.
               88  WS-NOT-EOF      VALUE 0.
               88  WS-EOF          VALUE 1.
           05  WS-ABORT-FLAG       PIC 9 VALUE 0.
               88  WS-CONTINUE     VALUE 0.
               88  WS-ABORT        VALUE 1.

       01  WS-COUNTERS.
           05  WS-READ-COUNT       PIC 9(9) COMP VALUE 0.
           05  WS-WRITE-COUNT      PIC 9(9) COMP VALUE 0.
           05  WS-ERROR-COUNT      PIC 9(9) COMP VALUE 0.
           05  WS-SKIP-COUNT       PIC 9(9) COMP VALUE 0.

       01  WS-ERROR-LIMITS.
           05  WS-MAX-ERRORS       PIC 9(5) COMP VALUE 100.
           05  WS-MAX-CONSECUTIVE  PIC 9(3) COMP VALUE 10.
           05  WS-CONSEC-ERRORS    PIC 9(3) COMP VALUE 0.

       01  WS-ERROR-LOG-REC.
           05  EL-TIMESTAMP        PIC X(19).
           05  FILLER              PIC X VALUE '|'.
           05  EL-SEVERITY         PIC X(01).
           05  FILLER              PIC X VALUE '|'.
           05  EL-COMPONENT        PIC X(15).
           05  FILLER              PIC X VALUE '|'.
           05  EL-ERROR-CODE       PIC X(08).
           05  FILLER              PIC X VALUE '|'.
           05  EL-MESSAGE          PIC X(80).
           05  FILLER              PIC X VALUE '|'.
           05  EL-RECORD-NUM       PIC 9(09).
           05  FILLER              PIC X VALUE '|'.
           05  EL-DATA-VALUE       PIC X(60).

       01  WS-INPUT-FIELDS.
           05  WS-IN-CUST-ID      PIC X(10).
           05  WS-IN-CUST-NAME    PIC X(30).
           05  WS-IN-AMOUNT       PIC X(10).
           05  WS-IN-TRANS-TYPE   PIC X.
           05  FILLER             PIC X(29).

       01  WS-CURRENT-PARAGRAPH   PIC X(30).
       01  WS-CURRENT-FILE        PIC X(20).

       PROCEDURE DIVISION.
       DECLARATIVES.

       INPUT-FILE-ERROR SECTION.
           USE AFTER STANDARD ERROR PROCEDURE ON INPUT-FILE.
       INPUT-FILE-ERROR-PARA.
           MOVE 'INPUT-FILE'      TO WS-CURRENT-FILE
           PERFORM 9100-LOG-IO-ERROR
           .

       OUTPUT-FILE-ERROR SECTION.
           USE AFTER STANDARD ERROR PROCEDURE ON OUTPUT-FILE.
       OUTPUT-FILE-ERROR-PARA.
           MOVE 'OUTPUT-FILE'     TO WS-CURRENT-FILE
           PERFORM 9100-LOG-IO-ERROR
           .

       END DECLARATIVES.

       MAIN-SECTION SECTION.
       0000-MAIN.
           PERFORM 1000-INITIALIZE
           IF WS-CONTINUE
               PERFORM 2000-PROCESS
                   UNTIL WS-EOF OR WS-ABORT
           END-IF
           PERFORM 3000-TERMINATE
           STOP RUN
           .

       1000-INITIALIZE.
           MOVE '1000-INITIALIZE'  TO WS-CURRENT-PARAGRAPH
           INITIALIZE WS-COUNTERS

           OPEN INPUT  INPUT-FILE
           IF WS-INPUT-STATUS NOT = '00'
               DISPLAY 'FATAL: CANNOT OPEN INPUT FILE, STATUS='
                   WS-INPUT-STATUS
               SET WS-ABORT TO TRUE
               MOVE 16 TO RETURN-CODE
           END-IF

           IF WS-CONTINUE
               OPEN OUTPUT OUTPUT-FILE
               IF WS-OUTPUT-STATUS NOT = '00'
                   DISPLAY 'FATAL: CANNOT OPEN OUTPUT, STATUS='
                       WS-OUTPUT-STATUS
                   SET WS-ABORT TO TRUE
                   MOVE 16 TO RETURN-CODE
               END-IF
           END-IF

           IF WS-CONTINUE
               OPEN OUTPUT ERROR-FILE
               OPEN OUTPUT ERROR-LOG
               PERFORM 2100-READ-INPUT
           END-IF
           .

       2000-PROCESS.
           MOVE '2000-PROCESS'     TO WS-CURRENT-PARAGRAPH
           MOVE INPUT-RECORD       TO WS-INPUT-FIELDS

           PERFORM 2200-VALIDATE-RECORD
           IF WS-RECORD-VALID
               PERFORM 2300-TRANSFORM-RECORD
               PERFORM 2400-WRITE-OUTPUT
               MOVE 0 TO WS-CONSEC-ERRORS
           ELSE
               PERFORM 2500-WRITE-ERROR
               ADD 1 TO WS-CONSEC-ERRORS
               PERFORM 2600-CHECK-ERROR-LIMITS
           END-IF

           PERFORM 2100-READ-INPUT
           .

       2100-READ-INPUT.
           READ INPUT-FILE
               AT END
                   SET WS-EOF TO TRUE
               NOT AT END
                   ADD 1 TO WS-READ-COUNT
           END-READ

           IF WS-INPUT-STATUS NOT = '00'
               AND WS-INPUT-STATUS NOT = '10'
               DISPLAY 'READ ERROR, STATUS='
                   WS-INPUT-STATUS
               SET WS-ABORT TO TRUE
           END-IF
           .

       2200-VALIDATE-RECORD.
           SET WS-RECORD-VALID TO TRUE

           IF WS-IN-CUST-ID = SPACES
               MOVE 'E' TO EL-SEVERITY
               MOVE 'BLANK CUSTOMER ID' TO EL-MESSAGE
               PERFORM 9200-LOG-DATA-ERROR
               SET WS-RECORD-INVALID TO TRUE
           END-IF

           IF WS-IN-AMOUNT NOT NUMERIC
               MOVE 'E' TO EL-SEVERITY
               MOVE 'NON-NUMERIC AMOUNT' TO EL-MESSAGE
               MOVE WS-IN-AMOUNT TO EL-DATA-VALUE
               PERFORM 9200-LOG-DATA-ERROR
               SET WS-RECORD-INVALID TO TRUE
           END-IF
           .

       2300-TRANSFORM-RECORD.
      *    Business transformation logic here
           CONTINUE
           .

       2400-WRITE-OUTPUT.
           WRITE OUTPUT-RECORD FROM WS-INPUT-FIELDS
           IF WS-OUTPUT-STATUS NOT = '00'
               DISPLAY 'WRITE ERROR, STATUS='
                   WS-OUTPUT-STATUS
               SET WS-ABORT TO TRUE
           ELSE
               ADD 1 TO WS-WRITE-COUNT
           END-IF
           .

       2500-WRITE-ERROR.
           WRITE ERROR-RECORD FROM INPUT-RECORD
           ADD 1 TO WS-ERROR-COUNT
           ADD 1 TO WS-SKIP-COUNT
           .

       2600-CHECK-ERROR-LIMITS.
           IF WS-ERROR-COUNT >= WS-MAX-ERRORS
               DISPLAY 'MAX ERROR LIMIT REACHED: '
                   WS-MAX-ERRORS
               SET WS-ABORT TO TRUE
               MOVE 16 TO RETURN-CODE
           END-IF

           IF WS-CONSEC-ERRORS >= WS-MAX-CONSECUTIVE
               DISPLAY 'CONSECUTIVE ERROR LIMIT REACHED: '
                   WS-MAX-CONSECUTIVE
               SET WS-ABORT TO TRUE
               MOVE 16 TO RETURN-CODE
           END-IF
           .

       3000-TERMINATE.
           MOVE '3000-TERMINATE'   TO WS-CURRENT-PARAGRAPH

           DISPLAY '========================================='
           DISPLAY 'PRODFRAME - PROCESSING SUMMARY'
           DISPLAY '========================================='
           DISPLAY 'RECORDS READ:      ' WS-READ-COUNT
           DISPLAY 'RECORDS WRITTEN:   ' WS-WRITE-COUNT
           DISPLAY 'RECORDS IN ERROR:  ' WS-ERROR-COUNT
           DISPLAY 'RECORDS SKIPPED:   ' WS-SKIP-COUNT
           DISPLAY '========================================='

           IF WS-ABORT
               DISPLAY '*** PROCESSING ABORTED ***'
           END-IF

           CLOSE INPUT-FILE
           CLOSE OUTPUT-FILE
           CLOSE ERROR-FILE
           CLOSE ERROR-LOG

           EVALUATE TRUE
               WHEN WS-ERROR-COUNT = 0
                   MOVE 0 TO RETURN-CODE
               WHEN WS-ERROR-COUNT <= 10
                   MOVE 4 TO RETURN-CODE
               WHEN WS-ABORT
                   MOVE 16 TO RETURN-CODE
               WHEN OTHER
                   MOVE 8 TO RETURN-CODE
           END-EVALUATE
           .

       9100-LOG-IO-ERROR.
           MOVE 'S' TO EL-SEVERITY
           STRING 'I/O ERROR ON ' WS-CURRENT-FILE
               ' STATUS=' WS-INPUT-STATUS
               DELIMITED BY SIZE
               INTO EL-MESSAGE
           END-STRING
           PERFORM 9300-WRITE-LOG
           .

       9200-LOG-DATA-ERROR.
           MOVE WS-READ-COUNT TO EL-RECORD-NUM
           MOVE WS-CURRENT-PARAGRAPH TO EL-COMPONENT
           PERFORM 9300-WRITE-LOG
           .

       9300-WRITE-LOG.
           MOVE FUNCTION CURRENT-DATE(1:8) TO
               EL-TIMESTAMP(1:8)
           MOVE '-' TO EL-TIMESTAMP(5:1)
                       EL-TIMESTAMP(8:1)
           MOVE ' ' TO EL-TIMESTAMP(11:1)
           MOVE FUNCTION CURRENT-DATE(9:6) TO
               EL-TIMESTAMP(12:6)

           WRITE LOG-RECORD FROM WS-ERROR-LOG-REC
           INITIALIZE WS-ERROR-LOG-REC
           .

16.14 VSAM Extended Status Codes

On IBM z/OS, VSAM files return extended status information beyond the standard two-character file status code. When the first character of the file status is '9', the second character and additional VSAM-specific feedback codes provide diagnostic information that is essential for resolving VSAM file errors.

Accessing VSAM Return and Reason Codes

The VSAM return code and reason code are available through the file status variable and, on IBM Enterprise COBOL, through the VSAM return code areas. Some shops define an extended file status area:

       01  WS-FILE-STATUS.
           05  WS-STATUS-KEY-1      PIC X.
           05  WS-STATUS-KEY-2      PIC X.
       01  WS-VSAM-RETURN-CODE     PIC S9(4) COMP.
       01  WS-VSAM-COMPONENT-CODE  PIC S9(4) COMP.
       01  WS-VSAM-REASON-CODE     PIC S9(4) COMP.

Common VSAM Error Scenarios

File Status VSAM RC Description Common Cause
90 8 Logic error Sequence error on sequential PUT
93 8/148 Resource not available Dataset is held by another job
96 -- Missing DD DD statement not in JCL
97 8/96 Implicit VERIFY failed File was not closed properly
35 8/40 File not found Dataset does not exist
39 8/44 Attribute mismatch COBOL FD does not match file

Diagnosing VSAM File Status 39

File status 39 (attribute mismatch) is one of the most frequently encountered VSAM errors. It means the file attributes defined in your COBOL program (record length, key position, key length, file organization) do not match the attributes of the physical VSAM dataset:

       9100-DIAGNOSE-STATUS-39.
           DISPLAY '*** FILE STATUS 39: ATTRIBUTE MISMATCH ***'
           DISPLAY 'POSSIBLE CAUSES:'
           DISPLAY '1. RECORD CONTAINS does not match'
           DISPLAY '   VSAM record size (RECORDSIZE parameter)'
           DISPLAY '2. RECORD KEY position/length does not'
           DISPLAY '   match VSAM key definition (KEYS parameter)'
           DISPLAY '3. ORGANIZATION does not match VSAM type'
           DISPLAY '   (KSDS vs ESDS vs RRDS)'
           DISPLAY '4. RECORDING MODE does not match'
           DISPLAY '   (fixed vs variable length)'
           DISPLAY 'CHECK: IDCAMS LISTCAT of the dataset'
           DISPLAY '       against the COBOL FD and SELECT'
           .

Centralized VSAM Error Diagnosis

       9000-DIAGNOSE-FILE-ERROR.
           EVALUATE WS-STATUS-KEY-1
               WHEN '0'
                   DISPLAY 'SUCCESSFUL OR WARNING'
               WHEN '1'
                   DISPLAY 'AT END CONDITION'
               WHEN '2'
                   PERFORM 9010-INVALID-KEY-DIAGNOSIS
               WHEN '3'
                   PERFORM 9020-PERMANENT-ERROR-DIAGNOSIS
               WHEN '4'
                   PERFORM 9030-LOGIC-ERROR-DIAGNOSIS
               WHEN '9'
                   PERFORM 9040-VSAM-ERROR-DIAGNOSIS
           END-EVALUATE
           .

       9010-INVALID-KEY-DIAGNOSIS.
           EVALUATE WS-FILE-STATUS
               WHEN '21'
                   DISPLAY 'KEY SEQUENCE ERROR'
                   DISPLAY 'KEYS MUST BE IN ASCENDING ORDER'
                   DISPLAY 'CURRENT KEY: ' WS-CURRENT-KEY
                   DISPLAY 'PREVIOUS KEY: ' WS-PREVIOUS-KEY
               WHEN '22'
                   DISPLAY 'DUPLICATE KEY'
                   DISPLAY 'ATTEMPTED KEY: ' WS-CURRENT-KEY
               WHEN '23'
                   DISPLAY 'RECORD NOT FOUND'
                   DISPLAY 'SEARCHED KEY: ' WS-CURRENT-KEY
               WHEN '24'
                   DISPLAY 'BOUNDARY VIOLATION'
                   DISPLAY 'FILE MAY BE FULL'
           END-EVALUATE
           .

       9040-VSAM-ERROR-DIAGNOSIS.
           DISPLAY 'VSAM-SPECIFIC ERROR'
           DISPLAY 'FILE STATUS: ' WS-FILE-STATUS
           DISPLAY 'VSAM RETURN CODE: '
               WS-VSAM-RETURN-CODE
           DISPLAY 'VSAM COMPONENT CODE: '
               WS-VSAM-COMPONENT-CODE
           DISPLAY 'VSAM REASON CODE: '
               WS-VSAM-REASON-CODE
           DISPLAY 'CONSULT DFSMS MACRO INSTRUCTIONS'
               ' FOR VSAM CODES'
           .

16.15 Exception Handling in Called Programs

When a subprogram encounters an error, it must communicate that error to its caller. There are several patterns for doing this in COBOL.

Return Code Pattern

The simplest approach uses the RETURN-CODE special register:

      * In the called program:
       IDENTIFICATION DIVISION.
       PROGRAM-ID. VALIDSUB.

       DATA DIVISION.
       LINKAGE SECTION.
       01  LS-INPUT-DATA.
           05  LS-FIELD-1      PIC X(10).
           05  LS-FIELD-2      PIC 9(5).

       PROCEDURE DIVISION USING LS-INPUT-DATA.
       0000-VALIDATE.
           MOVE 0 TO RETURN-CODE

           IF LS-FIELD-1 = SPACES
               MOVE 8 TO RETURN-CODE
               GOBACK
           END-IF

           IF LS-FIELD-2 NOT NUMERIC
               MOVE 8 TO RETURN-CODE
               GOBACK
           END-IF

           GOBACK
           .

Error Structure Pattern

For more detailed error reporting, pass an error structure through the LINKAGE SECTION:

      * Error structure passed between caller and callee
       01  WS-ERROR-BLOCK.
           05  EB-RETURN-CODE  PIC S9(4) COMP VALUE 0.
           05  EB-ERROR-COUNT  PIC S9(4) COMP VALUE 0.
           05  EB-ERROR-TABLE.
               10  EB-ERROR-ENTRY OCCURS 10 TIMES.
                   15  EB-ERR-FIELD PIC X(30).
                   15  EB-ERR-MSG   PIC X(50).

      * In the calling program:
           INITIALIZE WS-ERROR-BLOCK
           CALL 'VALIDSUB'
               USING WS-INPUT-DATA
                     WS-ERROR-BLOCK
           END-CALL

           IF EB-RETURN-CODE NOT = 0
               PERFORM VARYING WS-ERR-IDX FROM 1 BY 1
                   UNTIL WS-ERR-IDX > EB-ERROR-COUNT
                   DISPLAY EB-ERR-FIELD(WS-ERR-IDX) ': '
                       EB-ERR-MSG(WS-ERR-IDX)
               END-PERFORM
           END-IF

Exception Propagation

When a subprogram calls another subprogram and encounters an error, it should propagate the error to its caller rather than attempting to handle it independently. The general principle is: handle errors at the highest level that has enough context to take appropriate action.

      * Mid-level subprogram that propagates errors upward
       IDENTIFICATION DIVISION.
       PROGRAM-ID. MIDLEVEL.

       DATA DIVISION.
       LINKAGE SECTION.
       01  LS-REQUEST-DATA    PIC X(100).
       01  LS-RESULT-DATA     PIC X(200).
       01  LS-ERROR-BLOCK.
           05  LS-RC           PIC S9(4) COMP.
           05  LS-ERROR-MSG    PIC X(80).

       PROCEDURE DIVISION USING LS-REQUEST-DATA
                                LS-RESULT-DATA
                                LS-ERROR-BLOCK.
       0000-MAIN.
           MOVE 0 TO LS-RC

           CALL 'LOWLEVEL'
               USING LS-REQUEST-DATA
                     LS-RESULT-DATA
                     LS-ERROR-BLOCK
           END-CALL

      *    If the low-level program set an error, propagate it
           IF LS-RC NOT = 0
               GOBACK
           END-IF

      *    Continue with mid-level processing
           PERFORM 1000-PROCESS
           GOBACK
           .

16.16 Exception Handling Across Platforms

GnuCOBOL Exception Handling

GnuCOBOL supports the standard COBOL exception handling mechanisms (DECLARATIVES, file status codes, ON SIZE ERROR, AT END, INVALID KEY) and additionally supports portions of the COBOL 2002 RAISE/RESUME mechanism.

      * GnuCOBOL supports the FUNCTION EXCEPTION-STATUS
      * intrinsic function to retrieve the last exception

           COMPUTE WS-RESULT = WS-A / WS-B
               ON SIZE ERROR
                   DISPLAY 'EXCEPTION: '
                       FUNCTION EXCEPTION-STATUS
               NOT ON SIZE ERROR
                   DISPLAY 'RESULT: ' WS-RESULT
           END-COMPUTE

GnuCOBOL also supports runtime error handling through environment variables:

# Trap runtime errors and display details
export COB_SET_DEBUG=Y

# Set the runtime error procedure
# (GnuCOBOL-specific, not standard COBOL)

Micro Focus COBOL

Micro Focus COBOL supports all standard exception handling features and adds its own extensions, including the ability to trap runtime errors using the CBL_ERROR_PROC library routine:

       CALL 'CBL_ERROR_PROC'
           USING BY VALUE 0
                 BY REFERENCE ERROR-HANDLER-ADDRESS
       END-CALL

16.17 Error Handling in Multi-Program Systems

Enterprise COBOL applications typically consist of many programs that call each other through CALL, CICS LINK, or DB2 stored procedures. Error handling in these systems requires a consistent, system-wide strategy.

Error Propagation Architecture

In a multi-program system, errors should propagate upward through the call chain until they reach a program that has enough context to take appropriate action. Lower-level utility programs should report errors but not make decisions about recovery. Higher-level controlling programs should decide whether to retry, skip, log, or terminate:

      * Level 3: Low-level utility (reports errors)
       IDENTIFICATION DIVISION.
       PROGRAM-ID. DATEUTIL.
       DATA DIVISION.
       LINKAGE SECTION.
       01  LS-INPUT-DATE      PIC X(8).
       01  LS-OUTPUT-DATE     PIC X(10).
       01  LS-RETURN-CODE     PIC S9(4) COMP.
       01  LS-ERROR-MSG       PIC X(80).

       PROCEDURE DIVISION USING LS-INPUT-DATE
                                LS-OUTPUT-DATE
                                LS-RETURN-CODE
                                LS-ERROR-MSG.
       0000-CONVERT.
           MOVE 0 TO LS-RETURN-CODE
           MOVE SPACES TO LS-ERROR-MSG

           IF LS-INPUT-DATE NOT NUMERIC
               MOVE 8 TO LS-RETURN-CODE
               MOVE 'DATE CONTAINS NON-NUMERIC DATA'
                   TO LS-ERROR-MSG
               GOBACK
           END-IF

      *    ... conversion logic ...
           GOBACK
           .

      * Level 2: Business logic (handles or escalates errors)
       IDENTIFICATION DIVISION.
       PROGRAM-ID. TRANPROC.
       WORKING-STORAGE SECTION.
       01  WS-DATE-RC         PIC S9(4) COMP.
       01  WS-DATE-MSG        PIC X(80).

       PROCEDURE DIVISION.
       2200-VALIDATE-DATE.
           CALL 'DATEUTIL'
               USING WS-TRANS-DATE
                     WS-FORMATTED-DATE
                     WS-DATE-RC
                     WS-DATE-MSG
           END-CALL

           IF WS-DATE-RC NOT = 0
      *        Business decision: log and skip, or escalate?
               IF WS-DATE-RC = 4
      *            Warning: use default date and continue
                   MOVE WS-DEFAULT-DATE TO WS-FORMATTED-DATE
               ELSE
      *            Error: record this transaction as rejected
                   MOVE WS-DATE-MSG TO WS-REJECT-REASON
                   PERFORM 2900-REJECT-TRANSACTION
               END-IF
           END-IF
           .

Consistent Error Interface

Define a standard error interface structure that all programs in the system use:

      * Standard error interface (copybook ERRIFACE.cpy)
       01  ERROR-INTERFACE.
           05  EI-RETURN-CODE  PIC S9(4) COMP.
               88  EI-SUCCESS  VALUE 0.
               88  EI-WARNING  VALUE 4.
               88  EI-ERROR    VALUE 8.
               88  EI-SEVERE   VALUE 12.
               88  EI-FATAL    VALUE 16.
           05  EI-ERROR-COUNT  PIC S9(4) COMP.
           05  EI-ERRORS.
               10  EI-ERROR-ENTRY OCCURS 20 TIMES.
                   15  EI-ERR-CODE  PIC X(8).
                   15  EI-ERR-SEV   PIC 9.
                   15  EI-ERR-FIELD PIC X(30).
                   15  EI-ERR-MSG   PIC X(80).
                   15  EI-ERR-DATA  PIC X(50).

When every program in the system uses the same error interface, error handling becomes predictable and error information can flow seamlessly from the lowest-level utility to the highest-level controlling program.

Centralized Error Logging Service

In large systems, implement error logging as a separate service that all programs call:

      * All programs call this centralized logger
       CALL 'ERRLOGSV'
           USING WS-LOG-REQUEST
           ON EXCEPTION
      *        If even the logger fails, fall back to DISPLAY
               DISPLAY 'LOGGER UNAVAILABLE: '
                   WS-LOG-REQUEST
       END-CALL

The centralized logger writes to a standard log file (or DB2 table, or MQ queue) with a consistent format that operations monitoring tools can parse and alert on.


16.18 Best Practices Summary

Exception handling is not a feature you add at the end of development. It must be designed into the program from the start. Here are the essential best practices:

File Status: Always

Every file in every program must have a FILE STATUS variable. Every I/O operation must be followed by a check of that variable. There are no exceptions to this rule.

      * REQUIRED for every file:
           SELECT EVERY-FILE
               ASSIGN TO EVERYFL
               FILE STATUS IS WS-EVERY-STATUS.

Use 88-Level Conditions for Status Codes

Define 88-level condition names for every file status value you expect to encounter. This makes the code self-documenting:

       01  WS-FILE-STATUS      PIC XX.
           88  FS-SUCCESS       VALUE '00'.
           88  FS-DUPLICATE     VALUE '02'.
           88  FS-EOF           VALUE '10'.
           88  FS-KEY-NOT-FOUND VALUE '23'.
           88  FS-DISK-FULL     VALUE '34'.
           88  FS-NOT-EXISTS    VALUE '35'.
           88  FS-ALREADY-OPEN  VALUE '41'.

Scope Terminators: Always

Always use explicit scope terminators (END-IF, END-EVALUATE, END-READ, END-WRITE, END-COMPUTE, END-CALL). This eliminates ambiguity about where exception phrases apply:

      * GOOD: Clear scope
           READ INPUT-FILE
               AT END
                   SET WS-EOF TO TRUE
               NOT AT END
                   ADD 1 TO WS-COUNT
           END-READ

      * BAD: Ambiguous scope with period termination
           READ INPUT-FILE
               AT END SET WS-EOF TO TRUE.

Error Limits

Production programs should have error limits that prevent runaway processing when data is corrupted:

       01  WS-MAX-ERRORS       PIC 9(5) VALUE 1000.
       01  WS-ERROR-COUNT      PIC 9(5) VALUE 0.

           IF WS-ERROR-COUNT >= WS-MAX-ERRORS
               DISPLAY 'ERROR LIMIT EXCEEDED'
               PERFORM 9000-CONTROLLED-ABORT
           END-IF

Controlled Shutdown

Never let a program crash without cleaning up. Always close files, write summary records, and set appropriate return codes:

       9000-CONTROLLED-ABORT.
           DISPLAY 'CONTROLLED ABORT INITIATED'
           PERFORM 3000-TERMINATE
           MOVE 16 TO RETURN-CODE
           STOP RUN
           .

Log Everything

Every error should be logged with enough information to diagnose the problem without needing to reproduce it: the timestamp, the program name, the paragraph where the error occurred, the file or data element involved, the actual data value that caused the error, and the record number or key.

Test the Error Paths

Error handling code that is never tested will fail when it is needed most. Create test data that exercises every error path: invalid data, missing files, full disks, duplicate keys, and out-of-range values.


Summary

This chapter covered the complete spectrum of COBOL error handling, from language-level features to platform-specific mechanisms:

  • The DECLARATIVES section provides automatic interception of I/O errors through USE AFTER STANDARD ERROR/EXCEPTION procedures, triggered by the runtime before control returns to your program.
  • File status codes are the fundamental mechanism for detecting I/O outcomes. The two-character status code covers successful completion (00-07), end-of-file (10-14), invalid key conditions (21-24), permanent errors (30-39), logic errors (41-49), and implementor-defined conditions (90-99).
  • Inline exception phrases (ON SIZE ERROR, ON OVERFLOW, INVALID KEY, AT END) provide statement-level error handling that keeps the error response close to the statement that caused it.
  • The COBOL 2002 RAISE/RESUME mechanism introduces structured exception handling with user-definable exception classes, explicit exception raising, and controlled resumption.
  • IBM Language Environment condition handling operates beneath the COBOL level, intercepting hardware and software conditions with configurable severity-based responses.
  • Return codes communicate success or failure between programs, following the standard 0/4/8/12/16 convention.
  • Common abend codes (S0C7, S0C4, S0C1, S0CB, S322, S806) each have specific causes and specific prevention techniques.
  • Error logging patterns provide structured, diagnosable records of every error encountered during processing.
  • Defensive programming -- validating input, initializing variables, checking boundaries, guarding against division by zero, and checking file status after every I/O operation -- prevents the majority of production errors.
  • A production error handling framework combines all these techniques into a cohesive program structure with error limits, controlled shutdown, and comprehensive summary reporting.

The difference between a junior COBOL programmer and a senior one is often measured not by the business logic they write, but by how thoroughly they handle the cases when things go wrong. In production systems that process millions of transactions daily, robust error handling is not optional -- it is the foundation of reliability.


Exercises

  1. File Status Exercise: Write a program that opens an indexed file with FILE STATUS, attempts to READ a record with a non-existent key, and handles the resulting status code 23 with a meaningful message.

  2. Arithmetic Protection: Write a program that reads a file of transaction records, computes a running average of the amounts, and handles division by zero (for the first record) and size overflow using ON SIZE ERROR.

  3. DECLARATIVES Practice: Write a program with DECLARATIVES that handles errors on three different files (input, output, and log), each with its own declarative section.

  4. Error Limit Framework: Extend the production framework from Section 16.13 to include a percentage-based error limit (abort if more than 5% of records have errors) in addition to the absolute error limit.

  5. Comprehensive Validation: Write a validation subprogram that accepts a customer record, validates every field (customer ID, name, address, date of birth, account balance), and returns both a return code and a table of error messages describing every validation failure found.