oracle/graal · error · ArrayIndexOutOfBoundsException
arrayOffset is beyond array length
Error message
arrayOffset is beyond array length
What it means
The upper-bound arm of the same Unsafe boundsCheck: after alignment, if offset > maxIndex - accessSize the access would read past the array's usable memory (including the alignment slack allowed for sub-word CAS). IllegalArrayAccessException('arrayOffset is beyond array length') is thrown.
Source
Thrown at espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_sun_misc_Unsafe.java:580
* @throws IllegalArrayAccessException if the access is out of bounds
*/
private static void boundsCheck(
@JavaType(Object.class) StaticObject o, long offset, long accessSize, EspressoLanguage language) throws IllegalArrayAccessException {
// offset = baseOffset + index * indexScale
assert o.getKlass().isArray();
Klass klass = o.getKlass();
int baseOffset = arrayBaseOffset(klass);
int indexScale = arrayIndexScale(klass);
if (offset < baseOffset) {
throw new IllegalArrayAccessException("arrayOffset is less than baseOffset");
}
/*
* Ensure memory is aligned for operations like sub-word CAS that may temporarily access
* memory just beyond array bounds.
*/
int maxIndex = alignUpToIntBytes(baseOffset + o.length(language) * indexScale);
if (offset > maxIndex - accessSize) {
throw new IllegalArrayAccessException("arrayOffset is beyond array length");
}
}
/**
* Thrown when {@link #boundsCheck} fails due to an out-of-bounds access.
*/
private static class IllegalArrayAccessException extends Exception {
@Serial private static final long serialVersionUID = 1L;
IllegalArrayAccessException(String msg) {
super(msg);
}
@SuppressWarnings("sync-override")
@Override
public final Throwable fillInStackTrace() {
return this;
}View on GitHub (pinned to a66e9ccd1d)
Solutions
- Clamp indices: ensure index*indexScale + accessSize <= array length * indexScale before the Unsafe call.
- Use bulk APIs (arraycopy, ByteBuffer) instead of per-element Unsafe access where possible.
- Audit hardcoded scale constants against Unsafe.arrayIndexScale for the actual element type.
Example fix
// before
unsafe.getLong(arr, base + i * 8); // i can be arr.length -> overrun
// after
if (i >= 0 && i < arr.length) {
unsafe.getLong(arr, base + i * 8L);
} Defensive patterns
Strategy: validation
Validate before calling
long base = unsafe.arrayBaseOffset(arr.getClass()); long scale = unsafe.arrayIndexScale(arr.getClass()); if (offset + accessSize > base + (long) arr.length * scale) throw new IndexOutOfBoundsException();
Prevention
- Range-check index against arr.length before every Unsafe access.
- Avoid stale offsets captured before arrays are resized/replaced.
When it happens
Trigger: Guest code using Unsafe with an index >= array.length, an accessSize that overruns the last element (e.g. getLong at the last int slot), or stale offsets after an array shrank/was reallocated.
Common situations: Serialization/copy libraries (Kryo-style) doing manual memory copies with Unsafe; off-by-one index math; race where another thread replaced the array between offset computation and access.
Related errors
- arrayOffset is less than baseOffset
- Branch target %d out of bounds
- Memory access is outside the boundaries of the allocated mem
- Update spans the word, not supported
- Cannot bind label to negative position %d
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/35a273e2f3e4c027.
Report an issue: GitHub.