Problem Overview
The 1769-L33ER CompactLogix controller can enter a major fault state with the message "Array subscript too large" (Major Fault Type 4, Code 20) when a user program indexes an array beyond its declared upper bound. In the documented field incident, the fault latched on Rung 8 of a routine that indexed into a 16-element array using a counter tag L_PunchCounter.ACC, which reached 16 before the fault tripped. The processor dropped into Program mode, the discrete outputs released, and the only way to recover was a manual key switch transition or a fault reset attempt after the logic was corrected.
[16] is out of bounds and triggers a non-recoverable major fault until the program is edited.The 1769 CompactLogix family is described in the official 1769 CompactLogix Controllers User Manual (publication 1769-UM011), which defines the controller's task model, memory layout, and fault handling behavior that drives every troubleshooting path described below.
Affected Hardware and Software
| Item | Specification |
|---|---|
| Controller catalog | 1769-L33ER |
| Series | CompactLogix 5370 L3 (1 MB / 2 MB user memory) |
| Ethernet | Dual 10/100 Mbps ports, Device Level Ring (DLR) capable |
| Programming environment | Studio 5000 Logix Designer (formerly RSLogix 5000) |
| Minimum firmware | Per Studio 5000 minor revision in use at install |
| Major fault type | Type 4 (Program) |
| Major fault code | Code 20 – Array subscript too large |
| Recovery class | Non-recoverable (unless logic prevents the indexed reference) |
Symptoms and Fault Identification
When the major fault latches, the controller status changes to Faulted and the OK LED flashes red. The Logix Designer Major Faults tab shows:
- Fault Type:
4 (Program) - Fault Code:
20 (Array subscript too large) - Fault Location: routine and rung number where the out-of-bounds reference executed (e.g., Rung 8)
- Source: the indexed tag path (e.g.,
Source_A[?])
Open the routine highlighted in the Fault Location field and capture a screen of the indexed instruction. The "??" under Source A of a SUB instruction usually reveals which tag is being indexed and is the fastest path to the array in question. From there, trace the index expression back to its source (in the documented case, a CTU counter's .ACC member).
Root Cause: Array Indexing Mismatch
The classic root cause is a one-off error between number of elements and highest legal index. Logix Designer arrays are zero-based:
- An array declared with [16] has legal indices 0 to 15.
- An array declared with [15] has legal indices 0 to 14.
- An array declared with [0..15] also has legal indices 0 to 15.
If a counter preset to 15 is used as the index and the counter is permitted to increment past its preset (because the .DN bit is not gated on the indexed rung), the .ACC can reach 16 on the very next increment. The expression array_tag[L_PunchCounter.ACC] then becomes array_tag[16], which is one element past the end of a 16-element array. The runtime catches this and trips the major fault.
The canonical ladder fragment that produces this fault:
Rung 6: [XIC Local:1:I.RangeActive.13] [CTU L_PunchCounter,0,15]
Rung 7: [EQU L_PunchCounter.ACC 16] [OTE reset_done]
Rung 8: [XIC reset_done] [MOV Source A:array[L_PunchCounter.ACC] Destination]
Rung 6 increments the counter on a rising edge of Local:1:I.RangeActive.13. Rung 7 detects .ACC == 16 and latches reset_done. Rung 8 reads array[16] — one element beyond the array — and the controller faults on the MOV before any RES instruction has a chance to clear .ACC.
Root Cause: UDT Array Corruption Caveat
If your array is inside a UDT (for example Press.Holes[16] where Press is a UDT) and the array is not the last member, an out-of-bounds write stomps onto whatever tag follows in the structure, and an out-of-bounds read returns corrupted data from the adjacent member. The processor does not fault, so the corruption can persist for hours or days before it surfaces as anomalous process behavior.
Always wrap UDT array indexing with an explicit range check on every rung that uses the index. Prefer the LIM instruction (low/high test) or a GRT/LES pair rather than relying on the runtime to catch the error.
Root Cause: AOI Download Bug in Older Studio 5000 Revisions
A secondary root cause that produces identical-looking symptoms was documented in field service work where Add-On Instructions (AOIs) were being downloaded into the controller. When the program was downloaded, AOI-internal array references would intermittently trip "Array subscript too large" faults even when the offline logic showed correct bounds. The behavior was traced to a Logix Designer minor revision that mishandled AOI array metadata during project download.
| Studio 5000 minor revision | AOI array behavior after download |
|---|---|
| v32.2 / v32.3 | AOI array references can fault intermittently after program download |
| v32.4 | AOI array references download and execute correctly |
Update both the engineering workstation and any parallel programming station to v32.4 or later, rebuild the AOI references, re-download, and verify the fault no longer appears on cold restart. Do not skip a full program download — Online Edits alone will not flush the corrupted AOI metadata that the older revision writes into the controller image.
Diagnostic Procedure
- Connect to the 1769-L33ER with Studio 5000 Logix Designer and go online.
- Open Controller Properties → Major Faults and record Fault Type 4, Code 20, and the faulted routine/rung.
- Open the faulted routine and identify the indexed instruction. Look for any tag of the form
some_tag[expression]. - Right-click the array tag and choose Monitor. Note the declared dimension (for example, [16] means 0..15).
- Watch the index expression live. Confirm that the value being supplied equals or exceeds the array length.
- Cross-reference every write to the index variable (counter
.ACC, arithmetic result, HMI-supplied integer) and verify none can exceedSIZE(array) - 1. - If the array is a member of a UDT, capture a memory dump (Controller Log → Export) and look for adjacent-tag corruption rather than a hard fault.
Solution 1: LIM Range Check on the Index
Insert a LIM instruction that proves the index is in range before the indexed rung executes. The LIM instruction passes power when the test value falls between the low and high limits inclusive.
[XIC enable_condition] [LES L_PunchCounter.ACC 16] *[XIC enable_condition] [MOV Source A:array[L_PunchCounter.ACC] Destination]
The LES (less than) on .ACC guarantees that the index never exceeds 15 (legal upper bound for a 16-element array). An equivalent LIM form is:
[XIC enable_condition] [LIM 0 L_PunchCounter.ACC 15] *[XIC enable_condition] [MOV Source A:array[L_PunchCounter.ACC] Destination]
This wraps the index in the inclusive range 0..15. If .ACC ever exceeds 15, power is removed from the rung and the out-of-bounds access is skipped entirely.
Solution 2: CLR or XIO Guard Before the Indexed Rung
When the process logic dictates that the counter must reach 16 to indicate "done," reset the counter at the moment of completion and use an XIO guard to prevent the indexed rung from ever running with .ACC = 16.
Rung 7 (replacement):
[XIC L_PunchCounter.DN] [CLR L_PunchCounter] [OTE reset_done]
Rung 8 (replacement):
[XIC reset_done] [XIO L_PunchCounter.DN] [MOV Source A:array[L_PunchCounter.ACC] Destination]
Two important caveats when using RES:
- A RES instruction writes 0 into the .ACC and clears the .EN enable bit. If a CTU sits on a rung whose preceding logic is true, clearing .EN can produce an extra increment on the next scan even though the input bit did not change. Use
[CLR L_PunchCounter.ACC]instead of RES if you need to zero the accumulator without disturbing .EN. - Place the CLR/RES rung before the indexed rung in the program file so the index is zeroed before any array reference is evaluated in the same scan.
Solution 3: Extend the Array with the SIZE Instruction
If the operator is producing parts that legitimately require more elements, grow the array and let Logix Designer propagate the new size. Use the SIZE instruction to capture the array length at runtime and drive an HMI input limit so the operator can never request more elements than the array can hold.
[SIZE(L_PunchCounter,0) PanelView.MaxHoles]
[LES PanelView.EnteredHoles PanelView.MaxHoles] *[MOV PanelView.EnteredHoles PanelView.AcceptedHoles]
Configure the PanelView Plus numeric input data entry limit to PanelView.MaxHoles - 1 so the operator cannot enter a value greater than the highest legal index. The SIZE instruction returns the number of elements, so if the array is later resized in Logix Designer, the HMI limit automatically tracks the new size on the next download — no manual re-entry required.
Solution 4: Studio 5000 Minor Revision Update
When the root cause is the AOI download bug rather than user logic:
- Confirm the current Studio 5000 version on the engineering workstation (Help → About).
- Install v32.4 or later from the official Rockwell Automation product download portal.
- Open the AOD/ACD project, allow the version upgrade prompt to complete, and resolve any cross-reference rebuild errors.
- Perform a full program download (not Online Edit) into the 1769-L33ER.
- Cycle controller power to flush volatile execution state and verify the AOI array references no longer fault on cold restart.
HMI Input Validation
Operator-entered values are a frequent upstream cause of array faults. Enforce limits at three layers:
| Layer | Technique | Effect |
|---|---|---|
| HMI numeric input | Set Min/Max on the data entry object (e.g., 0..15) | Blocks invalid entry at the keypad |
| PLC tag attribute | Configure @Max on the tag that the HMI writes |
Clamps the value written by any HMI client |
| Program logic | LES/LEQ guard on the rung that uses the value | Last-line protection against any over-range value |
Verification
- Force
L_PunchCounter.ACCto 16 from the Monitor window and confirm the controller does not fault. - Run the routine for one full cycle of the highest expected part length and watch the array index wrap correctly to 0.
- Cycle controller power to confirm the fix survives a cold restart.
- Watch Controller Log → Major Faults for 24 hours of production to confirm zero Type 4 / Code 20 entries.
- If an HMI cap is in use, attempt to enter a value one above the limit and confirm the entry is rejected.
Prevention Checklist
- Any CTU used as an array index must have its rung guarded by a LIM 0..SIZE-1 or LES check.
- Arrays inside UDTs require an explicit LIM guard on every indexed reference — runtime bounds checking does not protect UDT members.
- Pin Studio 5000 to a known-good minor revision (v32.4 or later for AOI-heavy programs) and control who can install updates on engineering workstations.
- Document the array dimension and the highest legal index in the routine header comment.
- Add an
SIZE-driven HMI input limit so the operator-facing maximum tracks array size automatically. - Use
CLR tag.ACCinstead ofRES tagwhen zeroing a counter without disturbing its.ENbit.
What does "Array subscript too large" mean on a CompactLogix L33ER?
It is Major Fault Type 4, Code 20, raised when a program indexes an array tag with a value greater than or equal to the array's declared length. A 16-element array has legal indices 0 through 15, so any reference to element 16 or higher trips the fault.
Why does my counter reach 16 when the preset is 15?
A CTU increments on every rising edge of its rung-in condition, not only up to its preset. The .DN bit goes true when .ACC reaches the preset, but .ACC continues to climb on subsequent rising edges unless the rung is gated or the counter is reset. If you use .ACC as an array index without a guard, the first increment past the preset causes the fault.
How do I prevent the fault without changing the counter?
Insert a LIM 0..(SIZE-1) test on the index before the indexed rung executes, or use a LES guard that drops power when .ACC equals the array length. Either approach keeps the indexed rung from ever running with an out-of-bounds value.
Does the runtime protect arrays inside a UDT?
No. Built-in bounds checking only applies to unique array tags. Arrays that are members of a UDT and are not the last member of that UDT will silently read or write adjacent UDT memory when over-indexed, without raising a major fault. Always add an explicit LIM range check on UDT array indices.
Which Studio 5000 revision fixes the AOI array download bug?
Field service work showed the AOI array download bug present in Studio 5000 v32.2 and v32.3 and corrected in v32.4. Updating Logix Designer to v32.4 or later and performing a full program download resolves the corrupted AOI array metadata.