CLIST developer on retainer: JSUBPAY ALLOCATE DISP(EXCL) on PAYROLL.BATCH.CONTROL holds dataset enqueue through error exit without FREE — PAYROLL2 gets IKJ56231I ALREADY IN USE

z/OS 2.4 • TSO/E 4.4 • CLIST • ALLOCATE DISP(EXCL) • ISRDDN ENQ • D GRS,CONTENTION • retainer billing

At Midwest Federal Savings Bank, the payroll processing team ran two TSO user profiles — PAYROLL1 and PAYROLL2 — so that two payroll analysts could submit the biweekly and supplemental payroll batch jobs independently. The system worked fine for years. Then one October morning PAYROLL1’s job submission failed partway through — JES2 was at its hold threshold and rejected the submit — and PAYROLL2, who needed to submit the supplemental payroll run, got a message she had never seen before: IKJ56231I DATA SET PAYROLL.BATCH.CONTROL ALREADY IN USE, RETRY LATER.

The dataset was still exclusively allocated by PAYROLL1’s TSO session. PAYROLL1 had logged off after the error. The session ended. But the dataset enqueue — a z/OS Global Resource Serialization (GRS) SYSDSN EXCL lock — stayed held for four hours and twenty-two minutes until a systems programmer intervened. The CLIST retainer consultant was called the same afternoon.

The JSUBPAY CLIST and the ALLOCATE without FREE

JSUBPAY is the payroll batch submission CLIST, stored in library PAYROLL.TSO.CLIST. When an analyst runs it from TSO, it performs three steps: allocate the job control dataset exclusively, write the JES2 job card and pay period parameters, then SUBMIT. The relevant section:

PROC 2 PAYDATE CYCLE
CONTROL NOMSG NOLIST
*
ALLOCATE DATASET('PAYROLL.BATCH.CONTROL') DISP(EXCL) SHR REUSE
IF &LASTCC NE 0 THEN GOTO ALLOCERR
*
OPENFILE PAYCTLDD OUTPUT
PUTFILE PAYCTLDD  &STR(//PAYRLRUN JOB (ACCT),'PAYROLL',CLASS=A,MSGLEVEL=(1,1))
PUTFILE PAYCTLDD  &STR(//  EXEC PGM=PAYMAINP)
PUTFILE PAYCTLDD  &STR(PAYDATE=&PAYDATE)
PUTFILE PAYCTLDD  &STR(CYCLE=&CYCLE)
CLOSFILE PAYCTLDD
*
SUBMIT 'PAYROLL.BATCH.CONTROL'
IF &LASTCC NE 0 THEN GOTO SUBMITERR   /* ← no FREE before this GOTO */
*
FREE DATASET('PAYROLL.BATCH.CONTROL')
WRITE JOB SUBMITTED SUCCESSFULLY
EXIT
*
ALLOCERR: WRITE ALLOCATE FAILED RC=&LASTCC
EXIT                                   /* ← no FREE here (ALLOC failed so none to free) */
*
SUBMITERR: WRITE SUBMIT FAILED RC=&LASTCC
EXIT                                   /* ← FREE is missing here */
END PROC

The bug is on the SUBMITERR exit path. ALLOCATE DATASET('PAYROLL.BATCH.CONTROL') DISP(EXCL) succeeded — the SYSDSN EXCL enqueue was acquired. The subsequent PUTFILE writes succeeded. The SUBMIT failed (JES2 RC=12, job entry subsystem hold threshold). Control branched to SUBMITERR, printed the error message, and exited. The FREE DATASET('PAYROLL.BATCH.CONTROL') line that exists on the success path was never reached. The TSO ALLOCATE command holds a dataset allocation — and its associated GRS EXCL enqueue — for the lifetime of the TSO session or until a FREE is issued. PAYROLL1 then logged off, but the TSO session had already ended with the dataset still allocated. z/OS held the enqueue for a configurable cleanup interval; on this system it was set to match the TSO session timeout, so the enqueue stayed for hours.

ISRDDN and D GRS,CONTENTION diagnosis

The retainer consultant logged in under a systems programmer profile and ran the ISRDDN command in ISPF. The Dataset Allocations display showed PAYROLL.BATCH.CONTROL with disposition EXCL, allocated to TSO session TSOPAY1 under user PAYROLL1 — even though PAYROLL1 appeared logged off. The allocation was a ghost from the abandoned session.

At the operator console, the command D GRS,CONTENTION confirmed:

ISG020I RESOURCE CONTENTION:
  SYSDSN  PAYROLL.BATCH.CONTROL
  OWN  TSOPAY1  EXCL
  WAIT TSOPAY2  EXCL   (1 WAITER)

PAYROLL2’s JSUBPAY CLIST was waiting at its own ALLOCATE DISP(EXCL) call, blocked behind the ghost enqueue from PAYROLL1. The systems programmer issued a VARY GRS,RELEASE,RESOURCE=(SYSDSN,PAYROLL.BATCH.CONTROL) to force-release the enqueue. PAYROLL2’s ALLOCATE immediately succeeded and the supplemental payroll job was submitted — four hours late.

Fix: FREE before every EXIT and GOTO on error paths

The fix required adding FREE DATASET('PAYROLL.BATCH.CONTROL') before every exit path that follows a successful ALLOCATE. The corrected CLIST:

PROC 2 PAYDATE CYCLE
CONTROL MAIN NOFLUSH                   /* don't abort on TSO error before cleanup */
*
ALLOCATE DATASET('PAYROLL.BATCH.CONTROL') DISP(EXCL) SHR REUSE
IF &LASTCC NE 0 THEN GOTO ALLOCERR    /* alloc failed — nothing to FREE */
SET &ALLOCATED = 1                     /* flag: ALLOCATE succeeded */
*
OPENFILE PAYCTLDD OUTPUT
PUTFILE PAYCTLDD  &STR(//PAYRLRUN JOB ...)
... (write parameters) ...
CLOSFILE PAYCTLDD
*
SUBMIT 'PAYROLL.BATCH.CONTROL'
IF &LASTCC NE 0 THEN GOTO SUBMITERR
*
FREE DATASET('PAYROLL.BATCH.CONTROL')  /* success path */
WRITE JOB SUBMITTED SUCCESSFULLY
EXIT
*
SUBMITERR: +
  FREE DATASET('PAYROLL.BATCH.CONTROL')   /* ← added */
  WRITE SUBMIT FAILED RC=&LASTCC
  EXIT
*
ALLOCERR: WRITE ALLOCATE FAILED RC=&LASTCC
EXIT
END PROC

Two additional hardening changes were applied: CONTROL MAIN NOFLUSH was added at the top so that if TSO itself raises an unexpected error during OPENFILE/PUTFILE/CLOSFILE, the CLIST continues executing rather than aborting — giving it a chance to reach a cleanup EXIT. An ATTN EXIT routine was also added to handle PA1 attention interrupts: if the analyst presses PA1 to abort mid-CLIST, the attention exit runs the FREE before terminating.

After the fix: ISRDDN showed zero EXCL enqueues on PAYROLL.BATCH.CONTROL after a test SUBMIT failure. D GRS,CONTENTION: no contention on the resource. IKJ56231I: never seen again in production.

How HourTab helps CLIST retainer consultants

The fix took 2 hours total. But the CLIST retainer consultant also spent 30 minutes walking the payroll manager through what a GRS enqueue is, why TSO ALLOCATE holds it, and why CONTROL MAIN NOFLUSH matters. That explanation time is invisible in a flat invoice but visible in a HourTab entry labeled “explanation + stakeholder walk-through.” The payroll manager appreciated knowing she was paying for education, not just keystrokes. She bookmarked the HourTab link and checked it herself before the next quarterly billing review.

HourTab turns a time-tracker CSV into a public retainer-hours URL your client can bookmark — no login, no portal, no “how many hours do I have left?” emails. Free for one retainer. $9/mo for up to ten.

FAQ: z/OS TSO CLIST developer retainers

What does a z/OS TSO CLIST developer on retainer typically do?

A z/OS TSO CLIST developer on monthly retainer covers CLIST procedure maintenance (PROC/END PROC parameter passing, SET variable assignment, WRITE/WRITENR for user messages, CONTROL MAIN NOFLUSH for error handling, GOTO for conditional branching, DO/END blocks for structured logic), TSO command invocation from CLIST (ALLOCATE for dataset allocation with DISP SHR/MOD/EXCL/OLD, FREE for dataset deallocation, SUBMIT for JCL job submission, LISTCAT for catalog entry inspection), ISPF dialog services invoked from CLIST (ISPEXEC DISPLAY PANEL for panel display, ISPEXEC SETMSG for message routing, ISPEXEC VGET/VPUT for shared variable pools, ISPEXEC SELECT CMD for nested CLIST invocation), CLIST error handling (ATTN EXIT routine for PA1 attention interrupt, ERROR EXIT routine for TSO error conditions, &LASTCC and &MAXCC return code inspection), dataset allocation conflict diagnosis (ISRDDN for active allocation display, D GRS,CONTENTION for global resource serialization enqueue waits, SYSDSN ENQ EXCL vs SHR contention), and TSO CLIST migration to REXX for sites standardizing on REXX exec procedures.

What CLIST debugging work is most commonly underlogged?

CLIST procedures that ALLOCATE a dataset with DISP(EXCL) but exit on error paths without FREE are the most systematically underlogged CLIST retainer work. Pattern: JSUBPAY CLIST library PAYROLL.TSO.CLIST; ALLOCATE DATASET(‘PAYROLL.BATCH.CONTROL’) DISP(EXCL) — exclusive enqueue on PAYROLL.BATCH.CONTROL; writes JES job card and parameters; SUBMIT ‘PAYROLL.BATCH.CONTROL’ — if &LASTCC NE 0 (JES2 queue full) → GOTO SUBMITERR → WRITE ‘SUBMIT FAILED’ → EXIT; FREE DATASET(‘PAYROLL.BATCH.CONTROL’) is absent on the GOTO path; SYSDSN EXCL enqueue on PAYROLL.BATCH.CONTROL held for entire PAYROLL1 TSO session; PAYROLL2 runs JSUBPAY → ALLOCATE DISP(EXCL) → IKJ56231I DATA SET PAYROLL.BATCH.CONTROL ALREADY IN USE, RETRY LATER; ISRDDN ENQ tab: SYSDSN PAYROLL.BATCH.CONTROL EXCL held by PAYROLL1; D GRS,CONTENTION: 1 waiter for PAYROLL.BATCH.CONTROL; fix: FREE DATASET(‘PAYROLL.BATCH.CONTROL’) before each EXIT and before each GOTO that leads to EXIT on error paths; also CONTROL MAIN NOFLUSH; EXCL enqueue: eliminated after error exit. Log must name the CLIST procedure, ALLOCATE DISP value, dataset name, missing FREE path, ISRDDN ENQ state, IKJ message, and before/after.

What are typical z/OS TSO CLIST developer retainer rates?

Entry-level CLIST developers with basic CLIST syntax reading, TSO ALLOCATE/FREE/SUBMIT command invocation, and &LASTCC return code checking typically bill at $60 to $100 per hour. Mid-level CLIST developers with CLIST structured error handling (CONTROL MAIN NOFLUSH, ATTN EXIT, ERROR EXIT routines), ALLOCATE DISP(EXCL/SHR/OLD/MOD) enqueue conflict diagnosis (ISRDDN ENQ display, D GRS,CONTENTION operator command, IKJ56231I message root cause), ISPF dialog services invocation (ISPEXEC DISPLAY, SETMSG, VGET/VPUT for shared variable pools), CLIST to REXX conversion planning, and TSO batch JCL submission automation (SUBMIT, JESMSGLG monitoring, WAIT keyword) typically bill at $88 to $148 per hour. Senior CLIST developers with ISPF dialog manager full-stack development (panel definitions, skeleton file tailoring, message definitions, ISPF table services), CLIST performance optimization (minimizing ISPEXEC overhead, batch vs. interactive CLIST execution), TSO/E customization (LOGON PROC, ALTLIB ACTIVATE, LIBDEF for dynamic library allocation), CLIST to REXX migration execution (translating SET to REXX assignments, GOTO to SELECT/WHEN, ATTN EXIT to SIGNAL ON HALT), and integration of CLISTs with z/OS System Automation routines typically bill at $128 to $215 per hour. Monthly retainer ranges: $2,400 to $5,000/month for CLIST maintenance, dataset enqueue diagnosis, and TSO automation fixes; $4,500 to $9,000/month for active CLIST/ISPF development, REXX migration, and TSO/E administration.

What should a z/OS TSO CLIST developer retainer agreement include?

A CLIST retainer agreement should specify: z/OS version (2.4, 2.5, 3.1) and TSO/E version — TSO/E built-in commands and CLIST features vary by version; whether the retainer covers CLISTs in TSO CLIST libraries (SYSPROC, ISPPLIB, site-specific CLIST libraries defined via ALTLIB) or only specific named CLIST procedures; which datasets are in scope for ALLOCATE/FREE lifecycle review — CLIST-allocated datasets with DISP(EXCL) or DISP(OLD) are highest risk for enqueue retention; whether ISPF dialog development is in scope (panel definitions, skeleton tailoring, ISPF message files, ISPF table services) vs. only command-level CLIST maintenance; whether CLIST to REXX migration is in scope; whether dataset enqueue conflict diagnosis is in scope (ISRDDN, D GRS,CONTENTION, D GRS,RES=SYSDSN analysis, operator intervention coordination); whether TSO/E security exits or RACF dataset access review are in scope for CLIST that allocates sensitive payroll or financial datasets; and whether CLIST test execution in batch (IKJEFT01 utility) is covered for regression testing changes without logging into TSO interactively.

How should z/OS TSO CLIST developer retainer hours be logged?

Log each CLIST retainer session with the z/OS version, CLIST name, CLIST library, TSO command involved, dataset name, DISP value, error condition, ISRDDN/D GRS state, and before/after metric. For ALLOCATE EXCL without FREE on error path: z/OS 2.4 TSO/E 4.4; CLIST JSUBPAY in library PAYROLL.TSO.CLIST; user PAYROLL1 interactive TSO session; ALLOCATE DATASET(‘PAYROLL.BATCH.CONTROL’) DISP(EXCL) — SYSDSN EXCL enqueue acquired; writes JES2 job card (//PAYRLRUN JOB ...) and pay period parameters (PAYDATE=10/10/2026, CYCLE=BW) to PAYROLL.BATCH.CONTROL; SUBMIT ‘PAYROLL.BATCH.CONTROL’ → &LASTCC = 12 (JES2 job entry subsystem at hold threshold); GOTO SUBMITERR → WRITE ‘SUBMIT FAILED RC=&LASTCC’ → EXIT; no FREE DATASET(‘PAYROLL.BATCH.CONTROL’) before GOTO SUBMITERR or before EXIT; SYSDSN EXCL enqueue on PAYROLL.BATCH.CONTROL held by PAYROLL1 for 4h 22m; PAYROLL2 runs JSUBPAY → ALLOCATE DISP(EXCL) → IKJ56231I DATA SET PAYROLL.BATCH.CONTROL ALREADY IN USE, RETRY LATER; ISRDDN ALLOCATIONS: PAYROLL.BATCH.CONTROL EXCL TSOPAY1; D GRS,CONTENTION: SYSDSN PAYROLL.BATCH.CONTROL 1 waiter; sysadmin VARY GRS,RELEASE — released enqueue; fix: FREE DATASET(‘PAYROLL.BATCH.CONTROL’) added before GOTO SUBMITERR (line 31) and before EXIT at SUBMITERR (line 38); also CONTROL MAIN NOFLUSH and ATTN EXIT cleanup routine; ISRDDN after SUBMIT failure: 0 EXCL enqueues on PAYROLL.BATCH.CONTROL; hours: 0.5h ISRDDN/D GRS diagnosis + 1h CLIST source edit + 0.5h IKJEFT01 batch test.

See also: REXX developer retainer · JCL developer retainer · CICS developer retainer