oracle/graal · error · IndexOutOfBoundsException

fromIndex = ${fromIndex}

Error message

fromIndex = ${fromIndex}

What it means

Thrown by AbstractListWithoutField.subList range checks when the start index is negative. It mirrors java.util.AbstractList semantics: subList(fromIndex, toIndex) requires 0 <= fromIndex <= toIndex <= size. A negative fromIndex is rejected before any sublist view is created.

Source

Thrown at espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/polyglot/collections/AbstractListWithoutField.java:439

     *           <p>
     *           The {@code listIterator(int)} method returns a "wrapper object" over a list
     *           iterator on the backing list, which is created with the corresponding method on the
     *           backing list. The {@code iterator} method merely returns {@code listIterator()},
     *           and the {@code size} method merely returns the subclass's {@code size} field.
     *
     * @throws IndexOutOfBoundsException if an endpoint index value is out of range
     *             {@code (fromIndex < 0 || toIndex > size)}
     * @throws IllegalArgumentException if the endpoint indices are out of order
     *             {@code (fromIndex > toIndex)}
     */
    public List<E> subList(int fromIndex, int toIndex) {
        subListRangeCheck(fromIndex, toIndex, size());
        return (this instanceof RandomAccess ? new RandomAccessSubList<>(this, fromIndex, toIndex) : new SubList<>(this, fromIndex, toIndex));
    }

    static void subListRangeCheck(int fromIndex, int toIndex, int size) {
        if (fromIndex < 0) {
            throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
        }
        if (toIndex > size) {
            throw new IndexOutOfBoundsException("toIndex = " + toIndex);
        }
        if (fromIndex > toIndex) {
            throw new IllegalArgumentException("fromIndex(" + fromIndex +
                            ") > toIndex(" + toIndex + ")");
        }
    }

    // Comparison and hashing

    /**
     * Compares the specified object with this list for equality. Returns {@code true} if and only
     * if the specified object is also a list, both lists have the same size, and all corresponding
     * pairs of elements in the two lists are <i>equal</i>. (Two elements {@code e1} and {@code e2}
     * are <i>equal</i> if {@code (e1==null ? e2==null :
     * e1.equals(e2))}.) In other words, two lists are defined to be equal if they contain the same

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Validate fromIndex >= 0 before calling subList and clamp to 0 when appropriate.
  2. Check for -1 sentinels (indexOf, lastIndexOf) before using them as fromIndex.
  3. Use Math.max(0, fromIndex) when a clamped window is acceptable.
  4. Wrap the call in try-catch on IndexOutOfBoundsException at API boundaries that accept raw user input.

Example fix

// before
List<E> page = list.subList(start, end); // start can be -1 from lastIndexOf

// after
int start = Math.max(0, rawStart);
if (rawStart == -1 /* not found */) return List.of();
List<E> page = list.subList(start, Math.max(start, end));
Defensive patterns

Strategy: validation

Validate before calling

if (fromIndex < 0 || toIndex > list.size() || fromIndex > toIndex) {
    throw new IllegalArgumentException("bad range: " + fromIndex + ".." + toIndex + " of " + list.size());
}

Prevention

When it happens

Trigger: Calling subList(negative, n) on any Espresso polyglot collections list (e.g. list.subList(-1, 3)); computing an offset that underflows (start - 1 when start == 0); passing a user-supplied paging start index without clamping.

Common situations: Pagination code computing 'int start = page * pageSize - pageSize' that goes negative for page 0; passing an index derived from lastIndexOf (which returns -1 on miss) directly to subList.

Related errors


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/1fb6ff4d9d8a24ee. Report an issue: GitHub.