GDGBIAS is one of those JCL controls that looks small on the JOB statement and then changes the meaning of an entire batch flow.
If a job creates and later reuses MY.GDG(+1), we can ask the following question:
- Should later steps keep using the same relative-to-absolute mapping established earlier in the job?
- or should z/OS recalculate the meaning of
(0)and(+1)at each step boundary?
That is exactly what GDGBIAS controls. This article discusses the GDGBIAS behaviour and shows JCL you can run to observe how GDGBIAS=JOB and GDGBIAS=STEP behave differently.
1. Lab goals
By the end of this article you should be able to:
- Explain the difference between
GDGBIAS=JOBandGDGBIAS=STEP - Predict how
(0)and(+1)are resolved after a prior step catalogs a new generation - Build a small test GDG and reproduce the difference in batch
- Recognize where
VOL=REF, passed data sets, and utility-driven catalog changes make the choice more important
2. Required environment
For the practical part, use:
- z/OS 2.1 or later
- JES2 or JES3
3. First things first
z/OS keeps an internal mapping between a GDG relative reference and the absolute generation name that will actually be allocated.
Assume that your catalog currently contains the current generation as:
&SYSUID..GDGTEST.BASE.G0010V00then the relative names are resolved as shown below:
(0)is mapped toG0010V00(unless there is Vnn with nn > 00)(+1)maps to the next generation, for exampleG0011V00
The question is what happens after a step in the same job catalogs that G0011V00 generation. And the GDGBIAS value will matter. GDGBIAS accepts two values (JOB and STEP) that provide different behaviours:
| Attribute | GDGBIAS=JOB | GDGBIAS=STEP |
|---|---|---|
| Resolution point | First GDG reference in the job | Start of each step |
| Mapping persistence | Preserved across later steps | Rebuilt at every step boundary |
Step 2 meaning of (0) after Step 1 created (+1) | Still the generation that was current before Step 1 | The generation just created by Step 1 |
Step 2 meaning of (+1) after Step 1 created (+1) | Still the generation created earlier in the job | A brand new next generation |
That means GDGBIAS=JOB gives step-to-step stability inside one job, while GDGBIAS=STEP gives step-to-step recataloging against current catalog state.
4. Practical setup: Define a disposable test GDG
Before comparing the two bias modes, create a small GDG base and one initial generation. Copy and submit the JCL below (you may need to adjust the JOB card to fit your installation requirements):
//GDGPREP JOB ,'GDG PREP',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//DELETE EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DELETE &SYSUID..GDGTEST.BASE GDG FORCE
IF LASTCC LE 8 THEN SET MAXCC = 0
/*
//DEFINE EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DEFINE GDG (NAME(&SYSUID..GDGTEST.BASE) LIMIT(5) NOEMPTY SCRATCH)
/*
//SEED EXEC PGM=IEFBR14
//G0001 DD DSN=&SYSUID..GDGTEST.BASE(+1),
// DISP=(NEW,CATLG,DELETE),
// SPACE=(TRK,(1,1)),
// DCB=(RECFM=FB,LRECL=80,BLKSIZE=0),
// UNIT=SYSDAVerify the starting point with ISPF 3.4 or LISTCAT. At this point you should have exactly one current generation under the base.:
//GDGLIST JOB ,'GDG LIST',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//LISTCAT EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
LISTCAT ENTRIES(&SYSUID..GDGTEST.BASE) ALL
/*6. GDGBIAS=JOB behaviour
Submit a two-step job that:
1. Creates a new (+1) generation 2. In the next step, tries to read (+1) as a cataloged data set
//GDGJOB JOB ,'GDG JOB BIAS',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID,
// GDGBIAS=JOB
//STEP1 EXEC PGM=IEFBR14
//NEWGEN DD DSN=&SYSUID..GDGTEST.BASE(+1),
// DISP=(NEW,CATLG,DELETE),
// SPACE=(TRK,(1,1)),
// DCB=(RECFM=FB,LRECL=80,BLKSIZE=0),
// UNIT=SYSDA
//STEP2 EXEC PGM=IEBGENER
//SYSPRINT DD SYSOUT=*
//SYSUT1 DD DSN=&SYSUID..GDGTEST.BASE(+1),DISP=SHR
//SYSUT2 DD SYSOUT=*
//SYSIN DD DUMMYAs expected outcome, STEP1 catalogs a new generation and STEP2 succeeds because:
- Under
GDGBIAS=JOB, the relative mapping built earlier in the job remains in effect - The
(+1)inSTEP2still means the same absolute generation created inSTEP1
This is the classic frozen-job-view behaviour.
7. GDGBIAS=STEP behaviour
Now submit the same job logic with step-level GDGBIAS:
//GDGSTEP JOB ,'GDG STEP BIAS',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID,
// GDGBIAS=STEP
//STEP1 EXEC PGM=IEFBR14
//NEWGEN DD DSN=&SYSUID..GDGTEST.BASE(+1),
// DISP=(NEW,CATLG,DELETE),
// SPACE=(TRK,(1,1)),
// DCB=(RECFM=FB,LRECL=80,BLKSIZE=0),
// UNIT=SYSDA
//STEP2 EXEC PGM=IEBGENER
//SYSPRINT DD SYSOUT=*
//SYSUT1 DD DSN=&SYSUID..GDGTEST.BASE(+1),DISP=SHR
//SYSUT2 DD SYSOUT=*
//SYSIN DD DUMMYThis time, STEP1 succeeds and catalogs a new generation but STEP2 fails allocation. Why does it fail?
- Under
GDGBIAS=STEP, z/OS clears the earlier mapping at the step boundary - When
STEP2starts, the generation just created inSTEP1has become the new(0) (+1)now means the next not-yet-created generationDISP=SHRon a not-yet-existing generation fails
8. Let’s prove that (0) also shifts under STEP
The previous two jobs show the effect on (+1). This next pair shows the effect on (0). First, create a new current generation:
//GDGMAKE JOB ,'GDG MAKE',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//STEP1 EXEC PGM=IEFBR14
//NEWGEN DD DSN=&SYSUID..GDGTEST.BASE(+1),
// DISP=(NEW,CATLG,DELETE),
// SPACE=(TRK,(1,1)),
// DCB=(RECFM=FB,LRECL=80,BLKSIZE=0),
// UNIT=SYSDAThen compare these two readers.
Reader with GDGBIAS=JOB:
//READJOB JOB ,'READ JOB',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID,
// GDGBIAS=JOB
//STEP1 EXEC PGM=IEBGENER
//SYSPRINT DD SYSOUT=*
//SYSUT1 DD DSN=&SYSUID..GDGTEST.BASE(0),DISP=SHR
//SYSUT2 DD SYSOUT=*
//SYSIN DD DUMMYReader with GDGBIAS=STEP in a multi-step flow:
//READSTEP JOB ,'READ STEP',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID,
// GDGBIAS=STEP
//STEP1 EXEC PGM=IEFBR14
//NEWGEN DD DSN=&SYSUID..GDGTEST.BASE(+1),
// DISP=(NEW,CATLG,DELETE),
// SPACE=(TRK,(1,1)),
// DCB=(RECFM=FB,LRECL=80,BLKSIZE=0),
// UNIT=SYSDA
//STEP2 EXEC PGM=IEBGENER
//SYSPRINT DD SYSOUT=*
//SYSUT1 DD DSN=&SYSUID..GDGTEST.BASE(0),DISP=SHR
//SYSUT2 DD SYSOUT=*
//SYSIN DD DUMMYWhat to observe:
- With
JOB,(0)in a later step still means the generation that was current before the job first resolved the base - With
STEP,(0)in the later step means the generation just created bySTEP1
That distinction matters whenever a job expects “current” to mean “current when the job started” rather than “current right now.”
9. Advanced edge case 1: the VOL=REF paradox
VOL=REF is one of the places where teams get surprised because the volume reference does not behave like a normal cached GDG DSN reference. The practical trap is this:
- Under
GDGBIAS=JOB, a laterDSN=base(+1)can still refer to the same absolute generation created earlier in the job - But a
VOL=REFreference can still be resolved from the step-time catalog view rather than the earlier job-level GDG mapping you expected
Use a pattern like this to reason about it:
//GDGVREF JOB ,'VOL REF',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID,
// GDGBIAS=JOB
//STEP1 EXEC PGM=IEFBR14
//OUT1 DD DSN=&SYSUID..GDGTEST.BASE(+1),
// DISP=(NEW,CATLG,DELETE),
// SPACE=(TRK,(1,1)),
// DCB=(RECFM=FB,LRECL=80,BLKSIZE=0),
// UNIT=SYSDA
//STEP2 EXEC PGM=IEFBR14
//IN1 DD DSN=&SYSUID..GDGTEST.BASE(+1),DISP=SHR
//VOLX DD DSN=&SYSUID..GDGTEST.WORK,
// DISP=(NEW,CATLG,DELETE),
// UNIT=SYSDA,
// SPACE=(TRK,(1,1)),
// VOL=REF=*.IN1What makes this worth calling out is that people often assume:
IN1is reopening the generation just created inSTEP1- therefore any volume inheritance based on that logical intent should remain aligned
That assumption is unsafe when GDGs are involved.
The safer practical rule is:
- If you need volume inheritance from a data set created earlier in the same job, prefer a concrete reference such as
VOL=REF=*.stepname.ddname - Do not assume
VOL=REF=MY.GDG(+1)will behave like the step-stable DSN reference you had in mind
10. Advanced edge case 2: cross-job concurrency contamination
The most architectural difference between JOB and STEP shows up when another workload updates the same GDG base while your job is still running.
Under GDGBIAS=JOB:
- Your job effectively works from a frozen in-job mapping once the base is first resolved
- Later external catalog updates do not change the meaning of your already-established relative references
Under GDGBIAS=STEP:
- Every step boundary is a new catalog consultation point
- If another job catalogs a new generation between your
STEP1andSTEP2, yourSTEP2can inherit that shifted baseline
That means STEP is not just “more current.” It is also more exposed to unrelated concurrent catalog activity.
Practical consequence:
- If your flow assumes “the same
(+1)I meant earlier is still the one I mean now,”JOBis usually safer - If your flow intentionally wants to follow the current catalog state at every step,
STEPis the correct choice, but you must design around that moving baseline
11. Advanced edge case 3: utility-driven catalog desynchronization
If a step uses a utility such as IDCAMS to manipulate GDG entries directly, it can change the catalog in a way that is not equivalent to ordinary JCL allocation sequencing.
For example:
//FIXCAT EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DELETE &SYSUID..GDGTEST.BASE(0)
/*Why this matters:
- Under
GDGBIAS=JOB, later JCL steps can continue to work from the earlier in-job mapping even though the catalog was changed underneath that mapping - Under
GDGBIAS=STEP, the next step boundary forces a fresh lookup and naturally reflects the new catalog state
This is one of the few places where STEP can look self-healing while JOB can look stubborn. The same persistence that protects a job from outside interference can also preserve a mapping that no longer matches the live catalog after utility-driven changes.
12. Passed data sets: practical safe and unsafe patterns
When the created generation is passed rather than immediately reopened from the catalog, the behaviour is easier to reason about if you code the handoff explicitly.
Safer handoff pattern:
//PASSJOB JOB ,'PASS GDG',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID,
// GDGBIAS=STEP
//STEP1 EXEC PGM=IEBGENER
//SYSPRINT DD SYSOUT=*
//SYSUT1 DD *
STEP1 WRITES THIS RECORD
/*
//SYSUT2 DD DSN=&SYSUID..GDGTEST.BASE(+1),
// DISP=(NEW,PASS,DELETE),
// SPACE=(TRK,(1,1)),
// DCB=(RECFM=FB,LRECL=80,BLKSIZE=0),
// UNIT=SYSDA
//SYSIN DD DUMMY
//STEP2 EXEC PGM=IEBGENER
//SYSPRINT DD SYSOUT=*
//SYSUT1 DD DSN=*.STEP1.SYSUT2,DISP=OLD
//SYSUT2 DD SYSOUT=*
//SYSIN DD DUMMYWhy this is safer:
- The receiving step uses the passed data set directly
- It does not depend on recalculating GDG relative numbering after a step boundary
Less safe pattern under GDGBIAS=STEP:
//SYSUT1 DD DSN=&SYSUID..GDGTEST.BASE(+1),DISP=SHRIf the generation was cataloged before the receiving step starts, that (+1) may no longer refer to the file you intended.
There is also a more subtle distinction between uncataloged and cataloged passes.
Uncataloged pass pattern:
- If a step creates
base(+1)withDISP=(NEW,PASS,DELETE), the catalog baseline does not move yet - The receiving step can safely use the passed DD reference because it is not asking z/OS to reinterpret the GDG through the catalog
Cataloged pass pattern:
- If the created generation is cataloged before the receiving step starts, the next step under
GDGBIAS=STEPsees a new current(0) - A receiving step that tries to reopen the data set through
DSN=base(+1)may now point to the next uncreated generation instead of the one just passed
The practical rule is simple:
- If you are passing a GDG generation between steps, prefer
*.step.ddover re-deriving the target from a relative GDG reference
13. GDG ALL concatenations
Referencing the bare GDG base name without an explicit relative generation produces a GDG ALL allocation.
Example:
//READALL EXEC PGM=IEBGENER
//SYSPRINT DD SYSOUT=*
//SYSUT1 DD DSN=&SYSUID..GDGTEST.BASE,DISP=SHR
//SYSUT2 DD SYSOUT=*
//SYSIN DD DUMMYThat tells z/OS to build a concatenation of active generations for the base.
Why GDGBIAS matters here:
- Under
JOB, the logical list behind that concatenation is tied to the job's earlier GDG view - Under
STEP, the concatenation is rebuilt from the current catalog view at the start of the step
So if an earlier step catalogs a new generation:
JOBtends to keep the earlier concatenation view stableSTEPtends to include the newer generation automatically in the later step's GDG ALL view
This matters in reporting or replay jobs where a bare base name is used as shorthand for “all current generations.”
14. Deferred step restarts
Restart behaviour is another place where the choice becomes architectural rather than stylistic.
Under GDGBIAS=JOB:
- The restarted execution rebuilds its in-memory view from the catalog state that exists at restart time
- If earlier steps in the original run already cataloged new generations, that restart-time baseline can differ from the baseline assumed by the original unrestarted flow
Under GDGBIAS=STEP:
- Step entry already assumes a fresh recalculation model
- Restart behaviour is therefore more naturally aligned with the design of the bias mode
This does not mean STEP is always better for restart. It means STEP is usually easier to reason about when restart semantics are expected to follow current catalog state without extra mental offset calculations.
15. How to choose
If we need to reduce the decision to one sentence:
- Choose
JOBwhen the job needs a stable internal snapshot - Choose
STEPwhen the job should rebind itself to the live catalog at each step boundary
Use GDGBIAS=JOB when:
- The job should operate against a stable in-job snapshot of GDG relative numbering
- Later steps must keep referring to the same absolute generations chosen earlier in the flow
- Concurrent external catalog updates should not change the meaning of your relative references mid-run
Use GDGBIAS=STEP when:
- Each step should reflect the current catalog state
- Utility-driven or restart-oriented processing benefits from step-boundary reevaluation
- You are prepared for
(0)and(+1)to shift after intermediate catalog activity
16. Coding and scope
GDGBIAS can be controlled at several levels:
- JES or job class default (JOBCLASS(n) GDGBIAS=...)
- Explicit override on the
JOBstatement
//DAILYJOB JOB (ACCT),'DATA PROCESSING',CLASS=A,GDGBIAS=STEPThe operator can modify the JES job class setting dynamically
$T JOBCLASS(A),GDGBIAS=STEPIf you omit it, the system or job-class default applies.
17. What to remember
The most important practical rule is simple:
GDGBIAS=JOBfreezes relative GDG meaning inside the jobGDGBIAS=STEPrecalculates relative GDG meaning at each step boundary
That is why GDGBIAS belongs in the architectural design of a batch flow, not as an afterthought on the job card.
18. Useful IBM documentation
If you want to validate the mechanics described in this article against IBM documentation, these are the most useful references:
- z/OS JCL Reference: JOB statement
- z/OS JCL parameter field summary
- z/OS JES2 command reference:
$T JOBCLASS - z/OS JES2 command reference:
$ADD JOBCLASS - z/OS JES2 job class overview
- z/OS DFSMS: Creating a non-SMS-managed generation data set
- z/OS DFSMS Access Method Services: DEFINE GENERATIONDATAGROUP
- z/OS basic concepts: What is a generation data group?
- z/OS message reference: IEFA111I