Quick answer
An index outside 0 to length−1 was used on an array. The #1 cause is a loop bound written with <= instead of <, running one iteration past the end. The exception message names the exact bad index and the array's actual length — read those two numbers first; the gap between them tells you exactly how far off the calculation was.
The exact error string
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException:
Index 5 out of bounds for length 5
at com.example.App.printAll(App.java:12)
at com.example.App.main(App.java:6)
Modern Java (8+) prints both numbers explicitly: the index that was attempted (5) and the array's actual length (5) — which immediately tells you this is the classic off-by-one, since valid indices for a length-5 array only run from 0 to 4. The stack trace's top frame names the exact line that performed the access, which is where to start looking regardless of which of the causes below applies.
Cause 1: an off-by-one loop bound (<= instead of <)
int[] scores = {90, 85, 77, 92, 88};
for (int i = 0; i <= scores.length; i++) { // ❌ runs i = 0..5 inclusive
System.out.println(scores[i]); // scores[5] is out of bounds
}
// ✅ use < so the loop stops at the last valid index (length - 1)
for (int i = 0; i < scores.length; i++) {
System.out.println(scores[i]);
}
This single character difference — <= versus < — accounts for a large share of every ArrayIndexOutOfBoundsException ever thrown. An array of length N has valid indices 0 through N-1; the length itself is exactly one past the last valid position, by definition. A loop condition of i <= arr.length lets the loop variable reach arr.length itself on its final iteration, which is always one index past the end. Java's enhanced for-loop (for (int score : scores)) sidesteps this entire class of bug by never exposing a raw index at all when you don't need one.
Cause 2: a hardcoded or cached index after the array was resized
int[] data = {1, 2, 3, 4, 5};
int lastIndex = data.length - 1; // 4 — valid for THIS array
data = java.util.Arrays.copyOf(data, 2); // "resized" — actually a NEW,
// shorter array is created
System.out.println(data[lastIndex]); // ❌ lastIndex (4) is now out of
// bounds for length 2
// ✅ recompute the bound from the current array, not a stale cached value
System.out.println(data[data.length - 1]);
Java arrays have a fixed length baked in at creation — there is no in-place resize operation. What looks like "resizing" (Arrays.copyOf, or manually allocating a new array and copying elements over) actually creates an entirely new array object and reassigns the variable to point at it; any index, length, or bound computed against the old array before that reassignment is now stale and can easily exceed the new array's actual size. This is a common source of subtle bugs specifically because the variable name (data) looks unchanged even though the object it refers to is completely different.
Cause 3: a negative index from a miscalculated offset
int[] recent = new int[10];
int windowSize = 15; // ❌ larger than the array itself
int start = recent.length - windowSize; // -5 — a negative index
System.out.println(recent[start]); // ❌ Index -5 out of bounds for length 10
// ✅ clamp the computed index to the valid range before using it
int safeStart = Math.max(0, recent.length - windowSize);
System.out.println(recent[safeStart]);
Java's bounds check is symmetric: an index below 0 fails exactly the same way an index at or above length does, since both are outside the single valid range. Negative indices typically come from arithmetic that assumes a smaller offset than what's actually being subtracted — a "look back N items from the end" calculation where N turns out to be larger than the array itself is the classic version of this. Clamping a computed index with Math.max(0, ...) (and symmetrically Math.min(length - 1, ...) for the upper bound) is the standard defensive fix wherever an index is derived from a calculation rather than a simple loop counter.
Cause 4: iterating in reverse and stopping at the wrong bound
int[] arr = {10, 20, 30};
for (int i = arr.length; i > 0; i--) { // ❌ i starts at arr.length (3)
System.out.println(arr[i]); // arr[3] is out of bounds
}
// ✅ start the reverse loop at length - 1
for (int i = arr.length - 1; i >= 0; i--) {
System.out.println(arr[i]);
}
Reverse iteration is a second, less common flavor of the same off-by-one family: starting the loop counter at arr.length instead of arr.length - 1 means the very first access is already one past the end, before the loop has done any useful work at all. Because the mistake is at the start of the loop rather than accumulating over iterations, it fails immediately and loudly on the very first pass, which at least makes it fast to spot once you know to check the initial value.
Common causes at a glance
| Symptom | Root cause | Fix |
|---|---|---|
| index equals the array's length exactly | loop condition uses <= instead of < | use <, or switch to an enhanced for-loop |
| index is a small negative number | an offset calculation subtracted too much | clamp with Math.max(0, ...) |
| index used to be valid, now isn't | the array was replaced by a differently-sized one | recompute bounds from the current array, don't cache them |
| fails on the very first loop iteration | reverse loop starts at length instead of length - 1 | start reverse loops at arr.length - 1 |
Why this happens: Java always bounds-checks array access at runtime
Unlike C, where an out-of-bounds array access silently reads or writes adjacent memory (a classic source of memory corruption and security vulnerabilities), the Java Virtual Machine checks every single array access against the array's actual length at runtime and throws immediately if the index falls outside the valid range. This is a deliberate safety guarantee baked into the platform: it trades a small, constant-time runtime check on every access for the elimination of an entire class of memory-safety bugs. The trade-off is that a bounds mistake in Java is always a loud, immediate exception rather than a silent, hard-to-trace corruption — which is precisely why this exception is so common in stack traces (it's doing its job, surfacing the bug the moment it happens) rather than a sign that something is unusually broken about the code.
Debugging checklist
- ✓ Read both numbers in the message — "Index N out of bounds for length M" — and compute
N - Mto see how far off it was - ✓ Check every loop bound for
<=where it should be< - ✓ If the index came from a calculation, clamp it with
Math.max/Math.minbefore using it - ✓ If the array was recently reassigned/resized, confirm no stale cached length/index is still in use
- ✓ Prefer the enhanced for-loop (
for (T x : arr)) whenever you don't actually need the index itself
Frequently Asked Questions
What does ArrayIndexOutOfBoundsException mean?
Code tried to read or write an array element at an index that isn't within the array's valid range of 0 to length-1 inclusive. Java arrays are fixed-length from the moment they're created, and every access is bounds-checked at runtime; an out-of-range index throws this exception immediately rather than reading adjacent memory the way an unchecked language might.
Why is the most common cause using <= instead of < in a loop?
A valid index for an array of length N ranges from 0 to N-1 — the length itself is one past the last valid index. A loop written as for (int i = 0; i <= arr.length; i++) runs one iteration too many, attempting arr[arr.length] on the final pass, which is always out of bounds by exactly one. This single off-by-one mistake (<= vs <) is the single most common cause of this exception in real code.
Why does the exception message show a negative index sometimes?
A negative index usually comes from a calculated position — array.length - someValue where someValue is larger than length, or iterating backward from the end and subtracting past zero. Java's bounds check rejects negative indices exactly the same way it rejects indices too large; there's a single valid range, and anything outside it on either side throws.
Why did this appear after I resized or shortened an array?
Java arrays have a fixed length set at creation — there's no way to grow or shrink one in place. "Resizing" actually means creating a new, differently-sized array (via Arrays.copyOf or a manual copy) and reassigning the variable to point at it. If other code still holds a cached index or length value computed against the old array, it can now be out of range for the new one.
Should I use ArrayList instead of a plain array to avoid this?
ArrayList doesn't eliminate bounds errors — get(index) on an ArrayList throws the closely related IndexOutOfBoundsException for exactly the same reason, an out-of-range index. What ArrayList does provide is dynamic resizing (add/remove change its size safely) and helper methods (contains, indexOf) that reduce how often you need to compute a raw index yourself, which indirectly reduces how often this class of bug occurs.
Can this happen from a concurrently modified array?
A plain array's length is fixed and can't change concurrently, but if the array reference itself is reassigned to a shorter array by another thread while a loop is mid-iteration using a previously-read length, the loop can run past the new array's bounds. This is rare with plain arrays (which are usually not reassigned concurrently) but common with shared mutable collections; synchronizing access or using a thread-safe collection type prevents it.
More Go, Rust & Java errors
Browse the full reference for Go, Rust, Java, and other backend language errors — exact message, cause, and fix.