A common misconception is that SFLNXTCHG is a field-level keyword that takes an indicator parameter identifying which field a user changed (for example SFLNXTCHG(45) on one field and SFLNXTCHG(46) on another). It is not. Per IBM’s DDS reference, SFLNXTCHG is a record-level keyword, it takes no parameters, and it does not identify individual changed fields.
Developers searching for “SFLNXTCHG RPG” are usually trying to do one of two different things, and mixing them up causes most of the confusion around this keyword:
- Make sure a subfile record that failed validation comes back around on the next
READCcycle so the user is forced to fix it. This is whatSFLNXTCHGactually does. - Know which specific field on a row the user changed, so they can write back only that field. This is a real, separate need.
SFLNXTCHGdoes not solve it; the DDSCHANGEkeyword or a before-image comparison in RPG does (both are covered below).
This article covers both, clearly separated. It is aimed at RPG developers building or maintaining update-capable maintenance subfiles who need either a reliable error-correction loop or field-level change detection for selective writes.
It covers SFLNXTCHG and READC processing only. It does not cover subfile paging mechanics (SFLPAG, SFLSIZ, SFLDSP, SFLCTL) — for that, see the IBM i RPG Subfile Programming: SFLCTL, SFLPAG, SFLDSP Paging article. It also does not re-teach the underlying file opcodes; those are covered in the RPGLE File I/O Opcodes Reference and the RPGLE CHAIN Opcode Reference.
Prerequisites
Before this article is useful, you should already be comfortable with:
- Basic DDS subfile structure: a subfile record format, a subfile control (
SFLCTL) format, and theSFLPAG/SFLSIZ/SFLDSP/SFLDSPCTLkeywords that make a subfile page and display. READCloop processing against a subfile — reading the records the system considers “changed” since the subfile was last read, with the relative record number (RRN) field named on the RPGSFILEkeyword identifying which row was read.- Free-format RPGLE basics:
dcl-f,dcl-s,dow/forloops, and theupdateopcode. - Basic field validation patterns: testing input values after
EXFMT/READCand setting error indicators or messages.
If any of those are unfamiliar, start with the paging article linked above before coming back here.
What SFLNXTCHG actually does
SFLNXTCHG is a record-level DDS keyword specified on the subfile record format itself (the SFL format), not on any individual field. It takes no parameters.
Per IBM’s DDS reference for this keyword (see Sources Consulted below), SFLNXTCHG is described as a record-level keyword used on the subfile record format, with no parameters, whose function is to force the workstation user to correct program-detected typing errors. It does this by causing a record to be marked as changed so that a get-next-changed operation (READC) must read that record again — even if the user did not actually touch it.
Option indicators are valid for this keyword — you control whether SFLNXTCHG is in effect for a given record by conditioning it with an indicator (e.g., 14 SFLNXTCHG) and turning that indicator on only when you want the record requeued. SFLNXTCHG cannot be specified together with SFLMSGRCD on the same record format — per the same IBM reference, the two keywords cannot be used together.
In plain terms: SFLNXTCHG is not a “what changed” detector. It is a “make the subfile hand this record back to me again” lever. The system already has a notion of “changed records” that READC consumes (records the user edited and that haven’t been re-read yet); SFLNXTCHG lets your program artificially re-flag a record as changed, regardless of whether the user edited it, so your READC loop sees it again on the next pass.
What READC actually returns
READC (“read next changed record”) retrieves the next subfile record that the device considers changed. It only works against an externally described WORKSTN file, and the record name given to READC must be a record format defined as a subfile via the SFILE keyword. IBM’s documented example comments describe the behavior directly: if there are changes in any of the records currently on the display, READC reads those changed records one by one; when no changed records remain, %EOF returns 1 (or the error/EOF indicator in positions 75–76 comes on, if you used that form instead of %EOF).
That “changed” state is ordinarily set by the 5250 data stream when the user actually modifies an input-capable field on the display and presses Enter (or another command key that triggers a read). SFLNXTCHG is the keyword IBM documents specifically for setting that same “changed” state on a subfile record programmatically, without the user touching the field again. (The field-level DSPATR(MDT) attribute also turns on a field’s modified data tag from the program, but its purpose is general field marking, not requeuing error records.)
Put together: READC is the consumer of the “changed” flag; SFLNXTCHG is a way for your program to set that flag on a record it wants reprocessed, in addition to the flag the display itself sets when the user edits something.
The real use case: requeuing error records for correction
The documented scenario for SFLNXTCHG is validation error correction in a maintenance subfile:
- The user keys changes into one or more subfile rows and presses Enter.
- Your
READCloop picks up the rows the display marked as changed (the user’s edits). - For each changed row, your program validates the input. Some rows pass; some fail (e.g., a quantity out of range, an invalid code).
- For rows that fail validation, you update the subfile record with an error indication (a message, reverse image, cursor positioning) and, critically, turn on the option indicator controlling
SFLNXTCHGfor that record before (or as part of) writing it back to the subfile. - You redisplay the subfile. The user may look at other rows, fix some things, and press Enter again — possibly without touching the error row at all, assuming they didn’t notice it or intend to come back to it.
- Because
SFLNXTCHGwas in effect on the error row, the nextREADCloop returns that row again anyway, even though the user didn’t re-key it. Your program can re-validate it and keep forcing it back until it’s actually fixed.
Without SFLNXTCHG, that error row would only come back through READC if the user happened to edit it again. If they left it alone, your next READC pass would see no changed records at all for that row, and the error could be silently skipped — the user presses Enter, nothing stops them, and a bad or unresolved row ships through.
DDS: correct placement
SFLNXTCHG goes on the subfile record format, conditioned by an option indicator you control from RPG:
A*%%TS SD 20261014 000000 VKUMAR
A R SFLREC SFL
A CUSTNO 6A O 3 2
A CUSTNAME 30A B 3 10
A QTYONHAND 7Y 0B 3 42
A UNITPRICE 9Y 2B 3 52
A 14 SFLNXTCHG
Indicator 14 here is an ordinary RPG-settable indicator. When your program turns on *IN14 before rewriting the subfile record, that specific record is force-flagged as changed; when *IN14 is off, the keyword has no effect on that record. You are not required to dedicate indicator 14 specifically — any option indicator your shop’s numbering convention allows works the same way.
Do not also code SFLMSGRCD on this same record format — IBM’s reference for SFLNXTCHG states the two keywords cannot be combined.
RPGLE: correct READC + SFLNXTCHG + requeue pattern
dcl-f ORDMNT workstn sfile(SFLREC : rrn);
// prefix(DB_) keeps the database fields separate from the same-named
// subfile fields, so CHAIN does not overwrite the values the user keyed.
dcl-f CUSTMAST keyed usage(*update) prefix(DB_);
// *IN14 is the built-in RPG indicator array element tied to DDS indicator 14
// (the SFLNXTCHG conditioning indicator above). It is tested/set directly;
// it cannot be declared as a user-defined dcl-ds subfield, because an
// identifier cannot start with '*'.
dcl-s rrn packed(4:0); // SFILE relative record number field
dcl-s qtyOk ind;
dcl-s priceOk ind;
dcl-s errMsg varchar(50);
dow not %eof(ORDMNT);
readc sflRec;
if %eof(ORDMNT);
leave;
endif;
qtyOk = (QTYONHAND >= 0);
priceOk = (UNITPRICE >= 0 and UNITPRICE < 999999.99);
if qtyOk and priceOk;
// Row is valid — commit it and let it drop out of the changed set normally.
chain CUSTNO CUSTMAST;
if %found(CUSTMAST);
DB_QTYONHAND = QTYONHAND;
DB_UNITPRICE = UNITPRICE;
update CUSTR; // CUSTR = CUSTMAST's record format name (assumed)
endif;
*in14 = *off;
else;
// Row failed validation — force it back through the next READC cycle
// even if the user doesn't touch it again.
*in14 = *on;
if not qtyOk;
errMsg = 'Quantity must not be negative.';
else;
errMsg = 'Unit price out of range.';
endif;
// errMsg holds the validation text for this row. Route it to wherever
// your screen design surfaces per-row errors (a message subfield on
// SFLREC, or *PSSR/send-program-message) — that field isn't modeled in
// this stripped-down example, so errMsg itself is never displayed here.
endif;
update SFLREC;
enddo;
Notes on this pattern:
READCreturns rows the display already considers changed (user edits) and, on later passes, any row your program force-flagged viaSFLNXTCHG. You don’t test two different signals for this —READCreturning the row at all is the signal;SFLNXTCHGonly affects whether a row comes back, not what you see when it does.- The option indicator (
*IN14above, tested/set directly as a built-in indicator — noinddsdata structure needed for this) is set by your program before theupdate SFLRECthat rewrites the row. Thatupdateis what makes the forced-changed state take effect for the nextREADCpass. - Clear the indicator (
*off) once a row actually passes validation, or it will keep coming back indefinitely even after it’s fixed. - This loop still relies on the
SFILErelative record number field (rrnabove). Per IBM’s RPG reference,READCplaces the RRN of the record it retrieves into that field, which is how the program knows which row it is working on.SFLNXTCHGdoesn’t replace that addressing mechanism; it works alongside it. - The
prefix(DB_)onCUSTMASTmatters: without it, the database file and the subfile would share the field namesQTYONHANDandUNITPRICE, and thechainwould overwrite the values the user just keyed before they were ever written to the database.
Secondary topic: detecting which specific field the user changed
The goal some readers have — “how do I know which field on this row the user actually edited, so I only write back that field” — is a real, distinct question. It is not answered by SFLNXTCHG. There are two approaches.
Option 1: the DDS CHANGE keyword
IBM’s DDS reference documents a separate keyword for this: CHANGE(response-indicator ['text']), described as a “record-level or field-level keyword” that sets on the specified response indicator for an input operation. At the field level, the indicator comes on when that field’s modified data tag (MDT) is on; at the record level, it comes on when any input-capable field in the format has its MDT on. IBM notes that the MDT is set when the user types in the field (even if they retype the same value) or when the program selects DSPATR(MDT), so the indicator means “the user typed here,” not “the value is different.” IBM’s SFL keyword page lists CHANGE among the record-level keywords valid on a (non-message) subfile record format, and does not list it among the keywords that are invalid at field level on a subfile record format.
A R SFLREC SFL
A QTYONHAND 7Y 0B 3 42CHANGE(31 'Qty changed')
A UNITPRICE 9Y 2B 3 52CHANGE(32 'Price changed')
UNVERIFIED_SOURCE: the exact runtime behavior of field-level CHANGE indicators when rows are retrieved one at a time with READC (that is, that the indicators returned reflect the specific row just read) was not confirmed against IBM documentation for this article. Test it on your release before relying on it.
Option 2: a before-image comparison in RPG
If you need to know whether the value actually changed (not merely that the user typed in the field), compare the current subfile values to a before-image your RPG program manages itself:
dcl-ds beforeImage qualified template;
qtyOnHand like(QTYONHAND);
unitPrice like(UNITPRICE);
end-ds;
dcl-ds saved likeds(beforeImage) dim(50);
// When first loading a row into the subfile (e.g., from a database read),
// save its original values indexed by subfile relative record number:
saved(rrn).qtyOnHand = QTYONHAND;
saved(rrn).unitPrice = UNITPRICE;
// Later, after READC returns the row:
if QTYONHAND <> saved(rrn).qtyOnHand;
// quantity field specifically was changed by the user
endif;
if UNITPRICE <> saved(rrn).unitPrice;
// price field specifically was changed by the user
endif;
This takes more code than the CHANGE keyword, but it detects real value changes rather than keystrokes, and it does not depend on display-file indicators. Whichever approach you use, keep this concern separate from SFLNXTCHG in your own code and documentation; they solve different problems.
Common mistakes and troubleshooting
- Assuming SFLNXTCHG tells you what changed. It doesn’t carry any field-level information at all. It is a boolean “requeue this record” lever, record-wide, not a per-field signal.
- Coding SFLNXTCHG per field, with a parameter. The keyword takes no parameters and is not valid at the field level. If your DDS source has
SFLNXTCHG(nn)on an individual field, that is not the documented keyword usage — remove it. - Combining SFLNXTCHG with SFLMSGRCD. IBM’s reference states these cannot be used together on the same record format. Choose one error-presentation strategy.
- Forgetting to turn the option indicator off once a row is fixed. If you leave the controlling indicator on, the row keeps coming back through
READCforever, even after the user corrects it, because your program keeps re-flagging it as changed on every rewrite. - Confusing “READC returned this row” with “the user just edited this row.” Once
SFLNXTCHGis in play, a row can come back fromREADCpurely because your program forced it, not because the user touched it. Don’t assume everyREADChit represents fresh user input — check your own error/retry state too. - Treating this as a concurrency or locking feature.
SFLNXTCHGonly affects which rows get re-read by your own job’sREADCloop. It says nothing about other jobs updating the same underlying database record concurrently; that is a separate concern requiring your own row-version or locking strategy.
Performance and security considerations
Performance. SFLNXTCHG adds negligible overhead — it is a display-file-level flag toggle, not a database operation. Its performance relevance is indirect: it lets you avoid letting bad data silently pass through unvalidated, which avoids the more expensive cleanup (data-correction runs, support tickets) of commit-and-fix-later approaches. It has no bearing on the CHAIN/UPDATE cost against your actual database file; that cost is governed by your own validation and I/O logic, not by this keyword.
Security. SFLNXTCHG is not an authorization or input-validation mechanism by itself. It only forces re-presentation of a record for correction; your program still owns the actual validation logic (range checks, cross-field checks, authority checks) that decides whether to force the requeue in the first place. Relying on SFLNXTCHG without real validation logic behind it accomplishes nothing — the keyword has no opinion about what counts as an error; your RPG code supplies that.
A realistic use case
A customer-order entry subfile lets clerks key quantity and unit price changes across many rows, then press Enter once to submit the whole page. The program validates each changed row: quantity must be non-negative, unit price must fall within a configured range. Most rows pass and are committed immediately. A handful of rows fail — say, a clerk fat-fingered a price as a negative number. The program marks those rows with an error message and turns on the SFLNXTCHG option indicator before rewriting them to the subfile. The clerk, scanning a long page, might not immediately notice the flagged row and could press Enter again intending to move to the next page. Because SFLNXTCHG is in effect on the failed row, it comes back through the next READC cycle regardless, the error is still active, and the clerk cannot accidentally submit the batch with that row unresolved. This is precisely the documented purpose of the keyword: preventing users from skipping past program-detected errors.
FAQ
Does SFLNXTCHG tell me which field the user changed?
No. It is record-level with no parameters and carries no per-field information. See the secondary topic above for how to detect per-field changes (the DDS CHANGE keyword, or a before-image comparison you manage in RPG).
Can I use SFLNXTCHG together with SFLMSGRCD?
No. IBM’s DDS reference states these two keywords cannot be specified together on the same record format.
Does SFLNXTCHG replace the subfile relative record number?
No. The RRN field named on the RPG SFILE keyword identifies which row you’re reading or rewriting (READC fills it in). SFLNXTCHG only affects whether that row is reported as changed on a subsequent READC. They serve different, complementary purposes. (Don’t confuse the SFILE RRN field with the DDS SFLRCDNBR keyword, which controls which page of the subfile is displayed.)
What happens to a record’s SFLNXTCHG state if I don’t rewrite it?
The keyword takes effect based on the option indicator at the time the record is written/updated to the subfile. If you never rewrite the record with the indicator on, you haven’t forced it to be re-flagged as changed.
Is there a field-level keyword for per-field change tracking?
Not SFLNXTCHG, but yes: the DDS CHANGE keyword can be specified at field level to set a response indicator when that field’s modified data tag is on (see Option 1 above). It signals that the user typed in the field, not that the value differs; use a before-image comparison when you need true value-change detection.
Sources Consulted
- https://www.ibm.com/docs/en/i/7.5.0?topic=s-sflnxtchg — IBM i 7.5 DDS Reference,
SFLNXTCHGkeyword. Confirmed:SFLNXTCHGis a record-level keyword (specified on the subfile record format) with no parameters; it forces the workstation user to correct program-detected typing errors by causing a record to be marked changed so a get-next-changed (READC) operation must read it again; option indicators are valid for this keyword;SFLNXTCHGcannot be specified withSFLMSGRCD. - https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_72/rzasd/zzreadc.htm — IBM i RPG reference,
READC(Read Next Changed Record) operation code. Confirmed:READCworks only with an externally describedWORKSTNfile against a record format defined as a subfile viaSFILE; it reads the next record the subfile considers changed; the documented example notes that if there are changes in any of the records listed on the screen,READCreads the changed records one by one;%EOF(or an indicator in positions 75–76) signals when no changed records remain. - https://www.ibm.com/docs/en/i/7.5?topic=80-change-change-keyword-display-files — IBM i 7.5 DDS Reference,
CHANGEkeyword for display files. Confirmed: “You use this record-level or field-level keyword to set on the specified response indicator for an input operation”; formatCHANGE(response-indicator ['text']); field-level form responds to that field’s MDT, record-level form to any input-capable field’s MDT; the MDT is set when the user types in the field or when the program selectsDSPATR(MDT). - https://www.ibm.com/docs/en/i/7.5?topic=80-sfl-subfile-keyword-display-files — IBM i 7.5 DDS Reference,
SFLkeyword. Confirmed:CHANGEandSFLNXTCHGare listed among the record-level keywords allowed on a non-message subfile record format; the keywords not valid at field level on a subfile record format are DATE, DFTVAL, ERRMSG, ERRMSGID, MSGID, and TIME. - https://www.ibm.com/docs/en/i/7.5?topic=keywords-sfilerecformatrrnfield — IBM i 7.5 RPG reference,
SFILE(recformat:rrnfield)keyword. Confirmed: the rrnfield must be numeric with zero decimal positions and large enough for the largest record number; the RRN of a record retrieved byREADCorCHAINis placed into it; it supplies the record number forWRITEto the subfile. - https://www.ibm.com/docs/en/i/7.4.0?topic=files-using-subfiles — IBM i 7.4 subfile programming topic. Checked for
SFLNXTCHGusage examples andREADCsemantics; it did not add detail beyond the pages above, so no claims in this article rely on it.
Verification Status
The technical claims in this article were checked against the IBM i DDS and RPG reference pages listed above. The SFLNXTCHG DDS fragment is marked IBM DOC CHECKED because its placement and keyword usage (record-level, no parameters, option-indicator controlled, not combinable with SFLMSGRCD) match the IBM reference text directly. The RPGLE READC/SFLNXTCHG example is marked STATIC CHECKED: reviewed for valid free-form RPGLE syntax and consistent with the documented keyword behavior, but not compiled or run on a live system; no compiler access was available. The CHANGE DDS fragment and the before-image comparison example are marked NOT TESTED and are simplified illustrations, not production-ready code. The per-row behavior of field-level CHANGE indicators under READC is UNVERIFIED_SOURCE. Before using any code from this article in production, compile and test it against a real display file and subfile on your target release, and re-check the DDS reference for your IBM i release level.