File Status Codes

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

This page is written against ISO/IEC 1989:2023 (edition 3, January 2023; an upcoming 4th edition is in ballot), whose file status table comes from COBOL 85 with additions in 2002 and 2023; against IBM Enterprise COBOL for z/OS 6.5 (PTFs through September 2026) on z/OS 3.2; and against GnuCOBOL 3.2 (28 July 2023, the latest release), with which every program on it was compiled with cobc -x -Wall and run on 2026-10-20 (and again with -std=ibm, which printed the same results). z/OS examples are labelled "z/OS; not run here" and are checked against the Language Reference and the Programming Guide.

Every file statement leaves a two-character code in the FILE STATUS item of the file. The code says whether the statement worked and, if not, which of a few dozen things went wrong. This page is the reference table for those codes, with the Enterprise COBOL and GnuCOBOL differences, the extra VSAM status code of z/OS, the codes that were provoked on purpose to see what the run time really returns, and a status-checking paragraph that a program can copy in once and use for every file. How the code interacts with AT END, INVALID KEY and declaratives is on Error Handling and Declaratives; the statements that produce the codes are on Sequential Files, Indexed Files and VSAM and Relative Files; the mapping of file names is on File Concepts, SELECT and FD. The examples use the Circulation public-library scenario.

Where the book is outdated: Taulli’s Modern Mainframe Development (2022, chapter 5) presents FILE STATUS as the way to learn the result of a file operation, which is still the right idea. Its details need two corrections that matter for status codes. The status item is a separate two-character item in WORKING-STORAGE, not a field declared inside the record group, where it would be overwritten by every READ. And a file is opened with OPEN INPUT: the statement written as "OPEN READ" in the book is a syntax error (see Sequential Files), and the status 35 it is said to return is what OPEN INPUT returns for a file that does not exist. The book does not list the codes, so the table below comes from the official manuals.

Declaring the status item

SELECT file-name ASSIGN TO assignment-name
    FILE STATUS IS data-name-1 [data-name-2].
  • data-name-1 is the COBOL file status key: a two-character alphanumeric or national item, or a two-digit zoned or national decimal item, defined in WORKING-STORAGE or LOCAL-STORAGE, never in the file’s own record. One status item can serve several files, but then the program must test it right after the statement, before the next file statement overwrites it; giving each file its own item is safer.

  • data-name-2 is the optional VSAM status code of Enterprise COBOL (see below). GnuCOBOL accepts a single item.

  • The run time stores the code after every OPEN, CLOSE, READ, WRITE, REWRITE, START and DELETE on that file, before any AT END, INVALID KEY or declarative runs. Statements that succeed set 00 (or another 0x code), so a program must never test the item once and assume it stays valid.

  • Without the clause the program cannot see the code. An error then goes to an applicable declarative, and if there is none, ends the run unit with a message (see the error handling page).

The classes

The first character is the class and the second refines it.

First digit Class What it tells the program

0

Successful completion

The statement worked. A second character other than 0 carries a remark (duplicate key, short record, missing optional file).

1

At end

There is no next record. Only a sequential READ (or READ NEXT) ends here.

2

Invalid key

A keyed statement could not be done because of the key: duplicate, not found, out of sequence or out of bounds.

3

Permanent error

The file or the device failed, or the file is not what the program says it is. Retrying does not help.

4

Logic error

The program used the statement in the wrong state: opened twice, read without opening, rewrote without reading, wrote a record of the wrong length.

5

Standard extensions

Record locking and report-printing conditions (GnuCOBOL: 51, 57).

9

Implementor-defined

The compiler vendor’s own codes. On z/OS these are VSAM and QSAM conditions, with the VSAM status code adding detail.

The full table

The table lists the codes of the standard and of Enterprise COBOL 6.5, with the cause, the statements that can raise them and the differences met with GnuCOBOL 3.2. "GC" means GnuCOBOL 3.2.0.

Code Meaning Typical cause Difference or note

Class 0: successful

00

Successful, no further information

Any statement that did what it should

Same everywhere.

02

Successful; a duplicate key was found

A write or rewrite on an indexed file with an alternate key that allows duplicates, or a read that has another record with the same alternate key

Enterprise COBOL: indexed files with alternate keys only. In the test below GC returned 00 for a write that created a duplicate alternate key, so do not depend on 02 there.

04

Successful; the record length does not match the file description

A READ of a record shorter or longer than the FD allows

Enterprise COBOL: with the VLR(COMPAT) option a length conflict on READ returns 00 instead.

05

Successful; the optional file was not present

OPEN of a file declared OPTIONAL; it was created for I-O and EXTEND

Enterprise COBOL does not use it for VSAM sequential files. GC: 05 on OPEN INPUT.

06

GC only: the record was truncated

A LINE SEQUENTIAL line longer than the record; the rest comes back as the next record

Not in the standard or in Enterprise COBOL.

07

CLOSE or OPEN with NO REWIND, REEL or UNIT on a file that is not on a reel or unit medium

CLOSE file WITH NO REWIND on a disk file

Rare.

09

GC only: bad data was read

A LINE SEQUENTIAL line that contains control characters, when line validation is on

Not in the standard or in Enterprise COBOL.

Class 1: at end

10

End of file

A sequential READ with no next record, or the first READ of an unavailable optional file

Same everywhere. AT END runs for this code.

14

The relative record number is too large for the key item

A sequential READ on a relative file whose slot number has more digits than the RELATIVE KEY item holds

Same.

Class 2: invalid key

21

Sequence error

The prime key was changed between READ and REWRITE, or records were written out of ascending order to an indexed file in sequential access

Confirmed in GC with both causes (see the results below).

22

Duplicate key

A write of a key that exists, or a write or rewrite that duplicates an alternate key that does not allow duplicates; on a relative file, a slot that is in use

Same.

23

Record not found

A random READ, START, REWRITE or DELETE of a key or slot that does not exist, or a START that finds no key in the file

Same. The most common status in lookups, and not an error in itself.

24

Boundary violation

A write beyond the limits of a relative or indexed file, or a sequential write to a relative file whose number has too many digits

Same.

Class 3: permanent error

30

Permanent error, no further information

An I/O error, a missing directory, a device failure

GC returned 30 for an OPEN OUTPUT in a directory that does not exist (below).

31

GC only: inconsistent file name

The file name is inconsistent (the run-time constant is COB_STATUS_31_INCONSISTENT_FILENAME)

Not used by Enterprise COBOL.

34

Boundary violation on a sequential file

A WRITE beyond the space the data set has (no extent left)

Same.

35

File not found

OPEN INPUT, I-O or EXTEND of a non-optional file that does not exist

Same everywhere. The most frequent status in a first run.

37

Open mode not supported

OPEN OUTPUT of a read-only file, no permission, or a device that cannot do the mode

GC: "permission denied".

38

File closed with lock

OPEN of a file that was closed WITH LOCK earlier in the run unit

Same.

39

File attributes conflict

The organization, record size or type, key position or blocking in the program differs from the file

GC: confirmed when an indexed file is reopened with a different record length (below).

Class 4: logic error

41

OPEN of a file that is already open

A second OPEN without a CLOSE

Same.

42

CLOSE of a file that is not open

A second CLOSE, or a CLOSE of a file whose OPEN failed

Same.

43

No preceding successful READ

REWRITE, or DELETE on a sequentially accessed file, not preceded by a successful READ

Same.

44

Boundary violation

A REWRITE with a record of a different size, or a record outside the RECORD IS VARYING limits

GC: confirmed for both causes.

46

No valid next record

A sequential READ after the previous one failed without an end condition, or after end of file

Same.

47

READ on a file not open INPUT or I-O

READ before OPEN

Same.

48

WRITE on a file not open OUTPUT, I-O or EXTEND

WRITE on a file opened INPUT

Same.

49

DELETE or REWRITE on a file not open I-O

REWRITE on a file opened INPUT

Same.

Class 5: extensions

51

GC: record locked

Another run unit holds the record

COBOL 2002 locking; not in Enterprise COBOL.

57

GC: LINAGE error (the run-time constant is COB_STATUS_57_I_O_LINAGE)

A problem with the LINAGE specification of a print file

See Reports and Report Writer.

61

GC: file sharing failure

The file is locked by another process

Not in Enterprise COBOL.

71

GC: bad character

A LINE SEQUENTIAL write of a record with control characters, when line validation is on

Confirmed below.

Class 9: implementor-defined (Enterprise COBOL)

90

No further information

With multithreading, a CLOSE by a thread that did not open the file; otherwise see the VSAM return codes (VSAM) or the DFSMS message (QSAM)

91

VSAM: password failure

GC also uses 91: file not available.

92

Logic error

93

Resource not available

All files except QSAM, for example a VSAM data set that is held by another job

95

Invalid or incomplete file information

All files except QSAM

96

No DD statement

VSAM: OPEN OUTPUT, or OPEN I-O or EXTEND of an optional file, without a DD statement; QSAM the same when CBLQDA(OFF) is in effect

The mapping rules are on the file concepts page.

97

VSAM OPEN succeeded, and the file integrity was verified

The open succeeded and the file integrity was verified; the program may continue

With VSAMOPENFS(SUCC) in effect this returns 00 instead.

98

OPEN failed

Invalid contents of an environment variable named by ASSIGN, or a dynamic allocation failure

Related to the Enterprise COBOL environment-variable assignment.

99

VSAMDB only: the RECORD KEY item is too small for the copied key value

VSAMDB is z/OS only.

9A

VSAMDB only: the resize of a dynamic-length record item failed

The same code can come from different statements and different file types. A program has to know which statement it just ran: status 23 after a random READ means "no such record", and after REWRITE it means that the record to replace is gone.

Source: Enterprise COBOL for z/OS 6.5 Language Reference: File status key; GnuCOBOL 3.2 Programmer’s Guide; the GnuCOBOL 3.2 sources (libcob/common.h) for the COB_STATUS_* list

Provoking the codes on purpose

The next program uses an indexed file with an alternate key, an optional file, a second indexed file in sequential access, and a file in a directory that does not exist. Each statement is followed by a DISPLAY of the status.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. STATTEST.
      * Provoke file status codes on purpose.
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT BOOK-IDX ASSIGN TO "BOOKIDX"
               ORGANIZATION IS INDEXED
               ACCESS MODE IS DYNAMIC
               RECORD KEY IS IDX-ID
               ALTERNATE RECORD KEY IS IDX-STATUS WITH DUPLICATES
               FILE STATUS IS WS-FS.
           SELECT OPTIONAL RENEWALS ASSIGN TO "RENEWALS"
               ORGANIZATION IS LINE SEQUENTIAL
               FILE STATUS IS WS-FS.
           SELECT BOOK-SEQ ASSIGN TO "BOOKSEQ"
               ORGANIZATION IS INDEXED
               ACCESS MODE IS SEQUENTIAL
               RECORD KEY IS SEQ-ID
               FILE STATUS IS WS-FS.
           SELECT BAD-DIR ASSIGN TO "/nonexistent/dir/BOOKOUT"
               ORGANIZATION IS LINE SEQUENTIAL
               FILE STATUS IS WS-FS.
       DATA DIVISION.
       FILE SECTION.
       FD  BOOK-IDX.
       01  IDX-RECORD.
           05  IDX-ID          PIC 9(6).
           05  IDX-STATUS      PIC X.
           05  IDX-TITLE       PIC X(30).
       FD  RENEWALS.
       01  RENEWAL-LINE        PIC X(30).
       FD  BOOK-SEQ.
       01  SEQ-RECORD.
           05  SEQ-ID          PIC 9(6).
           05  SEQ-TEXT        PIC X(10).
       FD  BAD-DIR.
       01  BAD-LINE            PIC X(10).
       WORKING-STORAGE SECTION.
       01  WS-FS               PIC XX.
       PROCEDURE DIVISION.
       0000-MAIN-LINE.
           OPEN OUTPUT BOOK-IDX
           DISPLAY "open output indexed:       " WS-FS
           MOVE 1 TO IDX-ID MOVE "A" TO IDX-STATUS
           MOVE "DATA STRUCTURES" TO IDX-TITLE
           WRITE IDX-RECORD
           DISPLAY "write book 1:              " WS-FS
           MOVE 2 TO IDX-ID MOVE "COBOL BASICS" TO IDX-TITLE
           WRITE IDX-RECORD
           DISPLAY "write book 2, same status: " WS-FS
           MOVE 1 TO IDX-ID
           WRITE IDX-RECORD
           DISPLAY "write book 1 again:        " WS-FS
           CLOSE BOOK-IDX
           OPEN INPUT BOOK-IDX
           MOVE 999 TO IDX-ID
           READ BOOK-IDX
           DISPLAY "read book 999:             " WS-FS
           READ BOOK-IDX NEXT
           DISPLAY "read next after failure:   " WS-FS
           MOVE 5 TO IDX-ID
           START BOOK-IDX KEY IS > IDX-ID
           DISPLAY "start key > 5:             " WS-FS
           MOVE 7 TO IDX-ID
           WRITE IDX-RECORD
           DISPLAY "write on INPUT file:       " WS-FS
           CLOSE BOOK-IDX
           OPEN I-O BOOK-IDX
           MOVE 1 TO IDX-ID
           READ BOOK-IDX
           MOVE 9 TO IDX-ID
           REWRITE IDX-RECORD
           DISPLAY "rewrite with changed key:  " WS-FS
           CLOSE BOOK-IDX
           OPEN INPUT RENEWALS
           DISPLAY "open optional, missing:    " WS-FS
           READ RENEWALS
           DISPLAY "read optional, missing:    " WS-FS
           CLOSE RENEWALS
           OPEN OUTPUT BOOK-SEQ
           MOVE 2 TO SEQ-ID WRITE SEQ-RECORD
           MOVE 1 TO SEQ-ID WRITE SEQ-RECORD
           DISPLAY "write keys 2 then 1:       " WS-FS
           CLOSE BOOK-SEQ
           OPEN I-O BOOK-SEQ
           DELETE BOOK-SEQ
           DISPLAY "delete without read:       " WS-FS
           CLOSE BOOK-SEQ
           OPEN OUTPUT BAD-DIR
           DISPLAY "open in missing directory: " WS-FS
           STOP RUN.

Compiled with cobc -x -Wall -I. stattest.cob (and with -std=ibm), no warnings, it prints:

open output indexed:       00
write book 1:              00
write book 2, same status: 00
write book 1 again:        22
read book 999:             23
read next after failure:   00
start key > 5:             23
write on INPUT file:       48
rewrite with changed key:  21
open optional, missing:    05
read optional, missing:    10
write keys 2 then 1:       21
delete without read:       43
open in missing directory: 30

Reading the results:

  • The duplicate alternate key. Book 2 was written with the same IDX-STATUS value as book 1 and the alternate key allows duplicates. The standard and Enterprise COBOL call for 02 there; GnuCOBOL 3.2.0 with the Berkeley DB back end returned 00.

  • 22 came from writing book 1 a second time. 23 came from the random read of book 999 and from the START for a key greater than 5 (no book has a higher key). The READ NEXT after the failed random read returned 00: after a failed random read the file position is not defined by the standard, so a program must not read on from there.

  • 48 is the WRITE on a file opened INPUT.

  • 21 appeared twice: when the prime key was changed (1 to 9) between READ and REWRITE, and when keys 2 and 1 were written in that order to a file in sequential access.

  • 05 and 10 belong to the optional file: the OPEN INPUT of the missing file is successful, and the first READ is at end.

  • 43 is a DELETE on a sequentially accessed file without a READ before it.

  • 30 is what GnuCOBOL returned for OPEN OUTPUT of a file in a directory that does not exist; the standard only says "permanent error", and some systems give 35 or 37 instead.

A second program covers the codes that need a differently shaped file: the indexed file reopened with a different record length, a read after end of file, control characters in a line sequential file, and variable-length records outside their limits.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. STAT2.
      * More status codes provoked on purpose.
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT BOOK-IDX ASSIGN TO "BOOKIDX"
               ORGANIZATION IS INDEXED
               ACCESS MODE IS DYNAMIC
               RECORD KEY IS IDX-ID
               FILE STATUS IS WS-FS.
           SELECT LOAN-FILE ASSIGN TO "LOANTRAN"
               ORGANIZATION IS LINE SEQUENTIAL
               FILE STATUS IS WS-FS.
           SELECT TEXT-FILE ASSIGN TO "TEXTFILE"
               ORGANIZATION IS LINE SEQUENTIAL
               FILE STATUS IS WS-FS.
           SELECT BADTEXT ASSIGN TO "BADTEXT"
               ORGANIZATION IS LINE SEQUENTIAL
               FILE STATUS IS WS-FS.
           SELECT REC-FILE ASSIGN TO "RECFILE"
               ORGANIZATION IS SEQUENTIAL
               FILE STATUS IS WS-FS.
       DATA DIVISION.
       FILE SECTION.
       FD  BOOK-IDX.
       01  IDX-RECORD.
           05  IDX-ID          PIC 9(6).
           05  IDX-TITLE       PIC X(10).
       FD  LOAN-FILE.
       01  LOAN-LINE           PIC X(30).
       FD  TEXT-FILE.
       01  TEXT-LINE           PIC X(10).
       FD  BADTEXT.
       01  BADTEXT-LINE        PIC X(10).
       FD  REC-FILE
           RECORD IS VARYING IN SIZE FROM 5 TO 20 CHARACTERS
               DEPENDING ON WS-LEN.
       01  REC-LINE            PIC X(20).
       WORKING-STORAGE SECTION.
       01  WS-FS               PIC XX.
       01  WS-LEN              PIC 99.
       PROCEDURE DIVISION.
       0000-MAIN-LINE.
           OPEN INPUT BOOK-IDX
           DISPLAY "reopen with other length:  " WS-FS
           OPEN INPUT LOAN-FILE
           READ LOAN-FILE
           READ LOAN-FILE
           READ LOAN-FILE
           READ LOAN-FILE
           READ LOAN-FILE
           DISPLAY "read at end:               " WS-FS
           READ LOAN-FILE
           DISPLAY "read again after at end:   " WS-FS
           CLOSE LOAN-FILE
           OPEN OUTPUT TEXT-FILE
           MOVE "AB" TO TEXT-LINE
           MOVE X"0107" TO TEXT-LINE(3:2)
           WRITE TEXT-LINE
           DISPLAY "write control characters:  " WS-FS
           CLOSE TEXT-FILE
           OPEN INPUT BADTEXT
           READ BADTEXT
           DISPLAY "read control characters:   " WS-FS
           CLOSE BADTEXT
           OPEN OUTPUT REC-FILE
           MOVE 10 TO WS-LEN
           MOVE "0123456789" TO REC-LINE
           WRITE REC-LINE
           MOVE 3 TO WS-LEN
           WRITE REC-LINE
           DISPLAY "write length 3 (min 5):    " WS-FS
           CLOSE REC-FILE
           OPEN I-O REC-FILE
           READ REC-FILE
           MOVE 12 TO WS-LEN
           REWRITE REC-LINE
           DISPLAY "rewrite with other length: " WS-FS
           CLOSE REC-FILE
           STOP RUN.

The file BADTEXT contains the line AB, a bell and a start-of-heading control character, and CD, followed by a line OK. Compiled with cobc -x -Wall -I. stat2.cob (and with -std=ibm), no warnings, it prints:

reopen with other length:  39
read at end:               10
read again after at end:   46
write control characters:  71
read control characters:   09
write length 3 (min 5):    44
rewrite with other length: 44
  • 39: BOOKIDX was created with a 31-byte record (key, status and title) by the previous program; the new FD has 16 bytes. The file attributes conflict with the program, so the open is refused.

  • 10 and 46: the fifth read of the four-record LOANTRAN is at end, and a sixth read has no valid next record.

  • 71 and 09: GnuCOBOL 3.2 validates the data of LINE SEQUENTIAL files by default. Writing control characters is refused with 71, and reading a line that holds them returns 09. Enterprise COBOL has neither code.

  • 44: a REWRITE of a record with 12 bytes over one of 10 bytes, and a WRITE of 3 bytes into a file whose records must have 5 to 20.

In GnuCOBOL 3.2.0 a WRITE of a record longer than the RECORD IS VARYING maximum returned 00 (the DEPENDING ON value was 30 and the record area has 20 bytes), so the upper bound must be checked by the program itself.

The VSAM status code on z/OS

For VSAM files Enterprise COBOL can return a second item with the detail that the access method reported:

FILE STATUS IS file-status vsam-status

vsam-status is a 6-byte group of three 2-byte binary fields (PIC S9(4) COMP-5): the return code, the function code and the feedback code of the access method. They hold meaningful values only when the file status is not 0x. A typical case is status 90 with feedback code 82 (hexadecimal 52); the meaning of each combination is in the VSAM and DFSMS manuals (z/OS; not run here):

       WORKING-STORAGE SECTION.
       01  BOOK-FS                 PIC XX.
       01  BOOK-VSAM-STATUS.
           05  VSAM-R15-RETURN     PIC S9(4) COMP-5.
           05  VSAM-FUNCTION       PIC S9(4) COMP-5.
           05  VSAM-FEEDBACK       PIC S9(4) COMP-5.

The SELECT entry then reads FILE STATUS IS BOOK-FS BOOK-VSAM-STATUS. The second item does not apply to QSAM files. A program that logs both items for every status other than 0x, 10 and 23 gives operations what they need to look the error up. GnuCOBOL has no VSAM and accepts only the one-item form.

A reusable status-check paragraph

Testing the status after every statement is easy to forget and tedious to write. The two copybooks below put the logic in one place. FSWS defines the working storage, and FSPROC is a paragraph, copied once at the end of the PROCEDURE DIVISION, that translates the code to a text and a severity (OK, expected, warning or fatal), and stops the run with return code 16 on a fatal one. A program moves the status, the file name and the statement name to the check area and performs the paragraph.

      * FSWS - working storage of the file status check (FSPROC)
       01  FS-CHECK.
           05  FS-CODE             PIC XX.
           05  FS-FILE-NAME        PIC X(10).
           05  FS-OPERATION        PIC X(8).
           05  FS-TEXT             PIC X(36).
           05  FS-SEVERITY         PIC X.
               88  FS-OK           VALUE "O".
               88  FS-EXPECTED     VALUE "E".
               88  FS-WARNING      VALUE "W".
               88  FS-FATAL        VALUE "F".

      * FSPROC - set FS-TEXT and FS-SEVERITY from FS-CODE; stop on
      * a fatal status. The caller moves the status to FS-CODE and
      * the file and statement names to FS-FILE-NAME / FS-OPERATION.
       9900-CHECK-STATUS.
           EVALUATE FS-CODE
               WHEN "00"
                   MOVE "successful" TO FS-TEXT
                   SET FS-OK TO TRUE
               WHEN "02"
                   MOVE "successful, duplicate alternate key"
                       TO FS-TEXT
                   SET FS-OK TO TRUE
               WHEN "04"
               WHEN "06"
                   MOVE "record or line length mismatch"
                       TO FS-TEXT
                   SET FS-WARNING TO TRUE
               WHEN "05"
                   MOVE "optional file not present" TO FS-TEXT
                   SET FS-OK TO TRUE
               WHEN "10"
                   MOVE "end of file" TO FS-TEXT
                   SET FS-EXPECTED TO TRUE
               WHEN "22"
                   MOVE "duplicate key" TO FS-TEXT
                   SET FS-EXPECTED TO TRUE
               WHEN "23"
                   MOVE "record not found" TO FS-TEXT
                   SET FS-EXPECTED TO TRUE
               WHEN "35"
                   MOVE "file not found" TO FS-TEXT
                   SET FS-FATAL TO TRUE
               WHEN OTHER
                   EVALUATE FS-CODE(1:1)
                       WHEN "2"
                           MOVE "key error" TO FS-TEXT
                       WHEN "3"
                           MOVE "permanent file error" TO FS-TEXT
                       WHEN "4"
                           MOVE "statement used out of order"
                               TO FS-TEXT
                       WHEN "9"
                           MOVE "system-defined error" TO FS-TEXT
                       WHEN OTHER
                           MOVE "unexpected status" TO FS-TEXT
                   END-EVALUATE
                   SET FS-FATAL TO TRUE
           END-EVALUATE
           IF FS-FATAL
               DISPLAY "FATAL " FUNCTION TRIM(FS-FILE-NAME) " "
                   FUNCTION TRIM(FS-OPERATION) " status " FS-CODE
                   ": " FUNCTION TRIM(FS-TEXT)
               MOVE 16 TO RETURN-CODE
               STOP RUN
           END-IF.

The severity grouping is a policy, not part of the language. Here the end of file (10), a missing record (23) and a duplicate key (22) are "expected" because lookups and updates produce them in normal operation, a short or truncated record is a warning, and everything else that is not a success is fatal. A real shop tunes the WHEN branches per file. The demonstration program reads the book master until the end of file, then opens a file that does not exist:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. FSDEMO.
      * Use of the reusable status-check paragraph.
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT BOOK-FILE ASSIGN TO "BOOKMAST"
               ORGANIZATION IS LINE SEQUENTIAL
               FILE STATUS IS WS-BOOK-FS.
           SELECT RENEWALS ASSIGN TO "RENEWALS"
               ORGANIZATION IS LINE SEQUENTIAL
               FILE STATUS IS WS-RENEW-FS.
       DATA DIVISION.
       FILE SECTION.
       FD  BOOK-FILE.
       01  BOOK-LINE           PIC X(60).
       FD  RENEWALS.
       01  RENEWAL-LINE        PIC X(30).
       WORKING-STORAGE SECTION.
       COPY FSWS.
       01  WS-BOOK-FS          PIC XX.
       01  WS-RENEW-FS         PIC XX.
       01  WS-COUNT            PIC 9(3) VALUE ZERO.
       PROCEDURE DIVISION.
       0000-MAIN-LINE.
           OPEN INPUT BOOK-FILE
           MOVE WS-BOOK-FS TO FS-CODE
           MOVE "BOOK-FILE" TO FS-FILE-NAME
           MOVE "OPEN" TO FS-OPERATION
           PERFORM 9900-CHECK-STATUS
           PERFORM UNTIL FS-EXPECTED
               READ BOOK-FILE
               MOVE WS-BOOK-FS TO FS-CODE
               MOVE "READ" TO FS-OPERATION
               PERFORM 9900-CHECK-STATUS
               IF FS-OK
                   ADD 1 TO WS-COUNT
               END-IF
           END-PERFORM
           DISPLAY WS-COUNT " books read, last status: "
               FUNCTION TRIM(FS-TEXT)
           CLOSE BOOK-FILE
           OPEN INPUT RENEWALS
           MOVE WS-RENEW-FS TO FS-CODE
           MOVE "RENEWALS" TO FS-FILE-NAME
           MOVE "OPEN" TO FS-OPERATION
           PERFORM 9900-CHECK-STATUS
           DISPLAY "not reached"
           STOP RUN.
       COPY FSPROC.

Compiled with cobc -x -Wall -I. fsdemo.cob (and with -std=ibm), no warnings, it prints, and ends with return code 16:

004 books read, last status: end of file
FATAL RENEWALS OPEN status 35: file not found

The paragraph is the right place to add an operator message, a log record, the VSAM status code on z/OS, and an abend (CALL "CEE3ABD" on z/OS). Two points make the pattern reliable. The program moves the status into FS-CODE immediately after the statement and before anything else touches the file, and the paragraph is performed after every OPEN, READ, WRITE, REWRITE, DELETE, START and CLOSE that the logic does not already handle with AT END or INVALID KEY. A program that has INVALID KEY on a keyed READ should still run the check on the other statuses: the phrase runs only for the invalid-key class (2x), and a status 30 or 93 would otherwise go unnoticed.

A USE AFTER ERROR declarative (see Error Handling and Declaratives) is the alternative that avoids a check after every statement, at the cost of less local control.

Common mistakes

  • Testing the status only after OPEN. Every later statement sets it again; check after each one that can fail.

  • A shared status item and a delayed test. With one item for several files, the second statement overwrites the first’s result.

  • The status inside the record. It is overwritten by the next READ; declare it in WORKING-STORAGE.

  • Treating 23 or 10 as errors. They are the normal answers of "not there" and "no more"; handle them as results.

  • Treating only 00 as success. 02, 04, 05 and on z/OS 97 are successful completions that a test NOT = "00" turns into false errors; test the first character, or use the severity from the check paragraph.

  • Reading on after a failed read or START. The position is undefined (46 or garbage).

  • Ignoring OPEN OUTPUT failures. A full or read-only device shows up as 30, 34 or 37 on the first WRITE or already at OPEN.

  • Depending on dialect codes. 06, 09, 71 and 61 exist only in GnuCOBOL; Enterprise COBOL has 9x codes that GnuCOBOL does not return. Put dialect-specific tests in one place, such as the check paragraph.

  • Copying the table from a book of another release. The VLR(COMPAT) and VSAMOPENFS options change which code a statement returns on z/OS.