oracle/graal · error · IndexOutOfBoundsException

toIndex = ${toIndex}

Error message

toIndex = ${toIndex}

What it means

Thrown by AbstractListWithoutField.subList range checks when toIndex exceeds the list's size. The sublist view must stay inside [0, size]; an end index past the last element is rejected immediately.

Source

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

     *           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
     * elements in the same order.
     *
     * @implSpec This implementation first checks if the specified object is this list. If so, it

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Clamp the end index: subList(start, Math.min(end, list.size())).
  2. Re-read size() at the moment of the call rather than caching it.
  3. For last-page paging compute int end = Math.min(list.size(), start + pageSize).
  4. Guard with if (from > list.size()) return empty before slicing.

Example fix

// before
List<E> page = list.subList(start, start + pageSize); // fails on last page

// after
int end = Math.min(start + pageSize, list.size());
List<E> page = list.subList(start, end);
Defensive patterns

Strategy: validation

Validate before calling

int end = Math.min(requestedEnd, list.size());
if (fromIndex < 0 || fromIndex > end) {
    throw new IllegalArgumentException("bad range: " + fromIndex + ".." + end);
}

Prevention

When it happens

Trigger: Calling subList(from, to) with to > size(), e.g. subList(0, 10) on a 5-element list; computing an end offset as start + count without clamping; calling subList on a list that shrank between the size check and the call (concurrent modification).

Common situations: Paging logic 'subList(page * size, (page + 1) * size)' on the last, partial page; slicing a list after another thread removed elements; copying java.util.List sample code onto Espresso collections with different sizes.

Related errors


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