COBOL File Handling: OPEN, READ, WRITE, REWRITE, and CLOSE

File I/O is at the heart of COBOL's purpose. Every batch job processes input files, updates master files, and writes output — all through COBOL's structured file handling verbs. Understanding the full lifecycle from SELECT through CLOSE, and the difference between sequential and random access patterns, is essential for production mainframe development.
What you'll learn in this guide:
- SELECT and FD declarations for QSAM sequential files and VSAM KSDS indexed files
- OPEN, READ, WRITE, REWRITE, DELETE, and CLOSE with AT END handling
- FILE STATUS codes and why checking them after every I/O operation is non-negotiable
- Sequential vs random vs dynamic access modes and when to use each
Next lesson: COBOL Table Handling: OCCURS, INDEXED BY, SEARCH, and SEARCH ALL →
File Declaration: SELECT and FD
Every file used by a program must be declared in two places: the SELECT statement in the FILE-CONTROL section of the ENVIRONMENT DIVISION, and an FD (File Description) entry in the FILE SECTION of the DATA DIVISION.
Sequential File (QSAM)
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT TRANSACTION-FILE
ASSIGN TO TRANSIN
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS WS-TRAN-STATUS.
DATA DIVISION.
FILE SECTION.
FD TRANSACTION-FILE
RECORDING MODE IS F
BLOCK CONTAINS 0 RECORDS
RECORD CONTAINS 80 CHARACTERS.
01 TRANSACTION-RECORD PIC X(80).ASSIGN TO TRANSIN connects the logical file name to the JCL DD statement named TRANSIN. The physical dataset path is specified in JCL — the COBOL program never knows the actual dataset name, only the ddname.
RECORDING MODE IS F means fixed-length records. FB (fixed blocked) is specified in JCL; the COBOL compiler sees F. BLOCK CONTAINS 0 tells the runtime to use the block size from the JCL DD statement or dataset label.
VSAM KSDS File (Random Access)
FILE-CONTROL.
SELECT CUSTOMER-MASTER
ASSIGN TO CUSTMST
ORGANIZATION IS INDEXED
ACCESS MODE IS DYNAMIC
RECORD KEY IS CM-CUSTOMER-ID
ALTERNATE RECORD KEY IS CM-SSN
WITH DUPLICATES
FILE STATUS IS WS-CUST-STATUS.
FD CUSTOMER-MASTER
RECORD CONTAINS 200 CHARACTERS.
01 CUSTOMER-MASTER-RECORD.
05 CM-CUSTOMER-ID PIC X(10).
05 CM-SSN PIC X(11).
05 CM-LAST-NAME PIC X(25).
05 CM-FIRST-NAME PIC X(15).
05 CM-BALANCE PIC S9(11)V99 COMP-3.
05 CM-STATUS PIC X(2).
05 CM-OPEN-DATE PIC 9(8).
05 FILLER PIC X(110).ORGANIZATION IS INDEXED defines a KSDS. ACCESS MODE IS DYNAMIC enables both sequential and random access within the same program. RECORD KEY IS CM-CUSTOMER-ID names the primary key — it must be a field within the FD record. ALTERNATE RECORD KEY provides a secondary access path.
FILE STATUS Codes
Always declare a FILE STATUS field and check it after every file operation:
WORKING-STORAGE SECTION.
01 WS-FILE-STATUSES.
05 WS-CUST-STATUS PIC XX VALUE SPACES.
88 CUST-OK VALUE '00'.
88 CUST-EOF VALUE '10'.
88 CUST-NOT-FOUND VALUE '23'.
88 CUST-DUP-KEY VALUE '22'.
88 CUST-LOCKED VALUE '9D'.
88 CUST-OPEN-ERR VALUE '90' '91' '92' '93' '94' '95'.Key FILE STATUS codes:
| Code | Meaning |
|---|---|
00 | Successful completion |
02 | Duplicate alternate key (non-fatal on READ) |
10 | End of file reached |
22 | Duplicate primary key on WRITE |
23 | Record not found on random READ/DELETE |
24 | Boundary violation (WRITE past end of relative file) |
30 | Permanent I/O error |
9D | Record locked by another task |
9x | VSAM-specific error — check VSAM return/reason codes |
OPEN
Files must be opened before any I/O. The mode determines what operations are permitted:
OPEN INPUT TRANSACTION-FILE. *> read-only
OPEN OUTPUT REPORT-FILE. *> write only (creates/replaces)
OPEN I-O CUSTOMER-MASTER. *> read and update (REWRITE/DELETE)
OPEN EXTEND AUDIT-LOG-FILE. *> append to end of sequential file
IF NOT CUST-OK
DISPLAY 'OPEN FAILED: ' WS-CUST-STATUS
PERFORM ABEND-OPEN-FAILURE
END-IF.Multiple files can be opened in a single statement:
OPEN INPUT CUSTOMER-FILE
PRODUCT-FILE
OUTPUT REPORT-FILE.Always check FILE STATUS immediately after OPEN. A 9x status on OPEN means the file is unavailable, misallocated, or the DD statement is missing — the program cannot proceed.
READ: Sequential
For sequential access, READ retrieves the next record in order:
READ-NEXT-RECORD.
READ CUSTOMER-MASTER
AT END SET CUST-EOF TO TRUE
NOT AT END PERFORM PROCESS-CUSTOMER-RECORD
END-READ.The INTO phrase copies the record into a working-storage area (recommended for structured access):
READ TRANSACTION-FILE INTO WS-TRANSACTION-WORK
AT END SET TRAN-EOF TO TRUE
END-READ.Without INTO, the data is available in the FD record area directly. Using INTO gives you a working-storage copy with full COMP-3 field access.
READ: Random
Random READ retrieves a specific record by key:
RANDOM-READ-CUSTOMER.
MOVE WS-SEARCH-ID TO CM-CUSTOMER-ID
READ CUSTOMER-MASTER
EVALUATE TRUE
WHEN CUST-OK
PERFORM PROCESS-FOUND-CUSTOMER
WHEN CUST-NOT-FOUND
MOVE 'CUSTOMER NOT ON FILE' TO WS-MESSAGE
SET WS-ERROR-FOUND TO TRUE
WHEN CUST-LOCKED
PERFORM HANDLE-RECORD-LOCKED
WHEN OTHER
PERFORM HANDLE-READ-ERROR
END-EVALUATE.Move the key value to the RECORD KEY field in the FD record before issuing the READ. The runtime uses that value to locate the record.
READ with Alternate Key
MOVE WS-SSN TO CM-SSN
READ CUSTOMER-MASTER KEY IS CM-SSN
INVALID KEY
PERFORM HANDLE-SSN-NOT-FOUND
NOT INVALID KEY
PERFORM PROCESS-FOUND-RECORD
END-READ.INVALID KEY is the older form of AT END/NOT AT END for keyed reads. Modern code prefers checking FILE STATUS via 88-level conditions.
READ: Sequential Browsing of VSAM (START + READ NEXT)
With ACCESS MODE IS DYNAMIC, you can position to a key and read sequentially from that point:
BROWSE-FROM-KEY.
MOVE WS-START-ID TO CM-CUSTOMER-ID
START CUSTOMER-MASTER
KEY >= CM-CUSTOMER-ID
IF NOT CUST-OK
PERFORM HANDLE-START-ERROR
EXIT PARAGRAPH
END-IF
PERFORM UNTIL CUST-EOF OR WS-BROWSE-DONE
READ CUSTOMER-MASTER NEXT
AT END SET CUST-EOF TO TRUE
END-READ
IF CUST-OK
PERFORM PROCESS-BROWSE-RECORD
END-IF
END-PERFORM.START positions the file cursor without reading a record. READ NEXT (or READ ... NEXT RECORD) then reads sequentially from that position.
WRITE
WRITE adds a new record to an output or I-O file:
*> Sequential output:
MOVE WS-REPORT-LINE TO REPORT-RECORD
WRITE REPORT-RECORD
AFTER ADVANCING 1 LINE.
*> VSAM random write:
MOVE WS-NEW-CUSTOMER-DATA TO CUSTOMER-MASTER-RECORD
WRITE CUSTOMER-MASTER-RECORD
INVALID KEY
MOVE 'DUPLICATE CUSTOMER ID' TO WS-ERROR-MSG
SET WS-ERROR-FOUND TO TRUE
NOT INVALID KEY
ADD 1 TO WS-RECORDS-WRITTEN
END-WRITE.AFTER ADVANCING n LINES controls printer spacing for report files. AFTER ADVANCING PAGE issues a form feed.
INVALID KEY fires when a duplicate primary key exists. Always handle it — a duplicate write to a KSDS without error handling silently fails and raises FILE STATUS 22.
REWRITE
REWRITE updates an existing record in place. For sequential files, it replaces the last record returned by READ. For VSAM random access, it replaces the record matching the current key:
UPDATE-CUSTOMER-BALANCE.
MOVE WS-ACCOUNT-ID TO CM-CUSTOMER-ID
READ CUSTOMER-MASTER
IF CUST-NOT-FOUND
PERFORM HANDLE-NOT-FOUND
ELSE IF CUST-OK
ADD WS-TRANSACTION-AMOUNT TO CM-BALANCE
REWRITE CUSTOMER-MASTER-RECORD
IF NOT CUST-OK
PERFORM HANDLE-REWRITE-ERROR
END-IF
END-IF.The sequence is always: READ → modify fields in the record area → REWRITE. Attempting REWRITE without a prior successful READ produces unpredictable results.
DELETE
DELETE removes a record from a VSAM file. For KSDS files opened I-O, DELETE requires a prior READ:
CLOSE-CUSTOMER-ACCOUNT.
MOVE WS-ACCOUNT-TO-CLOSE TO CM-CUSTOMER-ID
READ CUSTOMER-MASTER
IF CUST-OK
DELETE CUSTOMER-MASTER RECORD
IF NOT CUST-OK
PERFORM HANDLE-DELETE-ERROR
END-IF
END-IF.CLOSE
Always close files before the program ends:
FINALIZE-PROGRAM.
CLOSE CUSTOMER-MASTER
TRANSACTION-FILE
REPORT-FILE.Failing to CLOSE leaves enqueue locks on VSAM files, preventing other jobs from accessing them. On z/OS, abnormal program termination (ABEND) automatically releases locks, but a normal STOP RUN without CLOSE leaves files open.
Complete Batch Update Example
PROCEDURE DIVISION.
MAIN-LOGIC.
PERFORM OPEN-ALL-FILES
PERFORM READ-FIRST-TRANSACTION
PERFORM UPDATE-LOOP UNTIL TRAN-EOF
PERFORM CLOSE-ALL-FILES
PERFORM DISPLAY-TOTALS
STOP RUN.
OPEN-ALL-FILES.
OPEN INPUT TRANSACTION-FILE
OPEN I-O CUSTOMER-MASTER
OPEN OUTPUT EXCEPTION-REPORT
IF NOT TRAN-OK OR NOT CUST-OK OR NOT EXCPT-OK
DISPLAY 'OPEN FAILED - TERMINATING'
STOP RUN
END-IF.
UPDATE-LOOP.
MOVE WS-TRAN-ACCOUNT-ID TO CM-CUSTOMER-ID
READ CUSTOMER-MASTER
EVALUATE TRUE
WHEN CUST-NOT-FOUND
PERFORM WRITE-EXCEPTION-NOT-FOUND
WHEN CUST-OK
PERFORM APPLY-TRANSACTION
PERFORM REWRITE-CUSTOMER
WHEN OTHER
PERFORM WRITE-EXCEPTION-IO-ERROR
END-EVALUATE
PERFORM READ-NEXT-TRANSACTION.
APPLY-TRANSACTION.
ADD WS-TRAN-AMOUNT TO CM-BALANCE
ADD 1 TO WS-UPDATES-APPLIED.
REWRITE-CUSTOMER.
REWRITE CUSTOMER-MASTER-RECORD
IF NOT CUST-OK
PERFORM WRITE-EXCEPTION-REWRITE-ERROR
SUBTRACT WS-TRAN-AMOUNT FROM CM-BALANCE
END-IF.
CLOSE-ALL-FILES.
CLOSE TRANSACTION-FILE
CUSTOMER-MASTER
EXCEPTION-REPORT.Next Steps
File handling gives you the ability to read and update persistent data. The next capability is table handling — OCCURS, SEARCH, and INDEXED BY for in-memory arrays and lookup tables. See COBOL Table Handling, or return to the COBOL Mastery course.
Frequently Asked Questions
Q: What is the difference between sequential, indexed, and relative file organisation in COBOL?
Sequential files store records one after another and must be read from beginning to end — they are ideal for batch processing where every record is processed in order. Indexed files (VSAM KSDS on mainframes) store records sorted by a key field and support both sequential reading and random access by key — essential when you need to look up individual records by ID. Relative files store records by record number and support direct access by position — useful for fixed-size lookup tables. Indexed organisation is by far the most common for business data files.
Q: What does FILE STATUS tell you and which values should I always check?
FILE STATUS is a two-character data item that the COBOL runtime updates after every file I/O operation. '00' means success. '10' means end-of-file on a READ. '23' means record not found on a random READ. '22' means duplicate key on a WRITE. Any value starting with '9' is a system-specific error. Always check FILE STATUS after OPEN, READ, WRITE, REWRITE, DELETE, and CLOSE. Failing to check it is the most common source of silent data corruption bugs in COBOL programs — the program continues running as if the I/O succeeded when it did not.
Q: What is the difference between READ NEXT and READ with a key in COBOL indexed files?
READ NEXT (used after START or an initial READ) advances sequentially through an indexed file in key order — it is used for sequential processing of an indexed file. READ with the INTO or KEY phrase retrieves a specific record whose key value is in the key data item — it is used for random access. The two modes are independent: you can switch between random and sequential access on the same file within a single program run, which is how COBOL programs efficiently process both lookups and sequential scans of the same dataset.
Ready to Master COBOL?
This lesson is part of the COBOL Mastery Course — the complete reference from first program to production mainframe. 20 modules covering COBOL syntax, file handling, DB2, CICS, JCL, and modern features. Free, fresher to senior.
