hibernate/hibernate-orm · error · ArrayIndexOutOfBoundsException

negative index

Error message

negative index

What it means

Thrown by PersistentList.get(int) when a negative index is passed to a Hibernate-managed List mapped as an entity collection. Hibernate checks the index up front (before readElementByIndex, which may hit the database on an uninitialized lazy collection) and throws ArrayIndexOutOfBoundsException with the message 'negative index', matching the contract of java.util.List. Any caller doing positional access on an @OneToMany/List attribute can hit it.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentList.java:304

	@Override
	public void clear() {
		if ( isClearQueueEnabled() ) {
			queueOperation( new Clear() );
		}
		else {
			initialize( true );
			if ( ! list.isEmpty() ) {
				list.clear();
				dirty();
			}
		}
	}

	@Override
	public E get(int index) {
		if ( index < 0 ) {
			throw new ArrayIndexOutOfBoundsException( "negative index" );
		}
		return readElementByIndex( index ).evaluate( () -> list.get( index ) );
	}

	@Override
	public E set(int index, E value) {
		if (index<0) {
			throw new ArrayIndexOutOfBoundsException("negative index");
		}
		if ( isPutQueueEnabled()
				&& readElementByIndex( index ) instanceof Defined<E> element ) {
			final E old = element.result();
			queueOperation( new Set( index, value, old ) );
			return old;
		}
		else {
			write();
			return list.set( index, value );

View on GitHub (pinned to fad1729dce)

Solutions

  1. Validate index >= 0 (and index < size()) before calling get; reject with a domain-level error or clamp
  2. If the index comes from page/offset math, guard the inputs (page >= 1) before computing it
  3. Prefer iteration (for-each, streams) or Optional-returning finders over positional access on entity collections
  4. Wrap positional reads in one bounds-checked helper used everywhere instead of scattering raw get() calls

Example fix

// before
Item first = order.getItems().get(idx - 1); // idx == 0 -> -1

// after
int i = idx - 1;
List<Item> items = order.getItems();
if (i < 0 || i >= items.size()) {
    throw new IllegalArgumentException("index out of range: " + i);
}
Item first = items.get(i);
Defensive patterns

Strategy: validation

Validate before calling

List<Item> items = entity.getItems();
if (index < 0 || index >= items.size()) {
    throw new IllegalArgumentException("index out of range: " + index + " for size " + items.size());
}
Item item = items.get(index);

Try / catch

try {
    Item item = entity.getItems().get(index);
} catch (IndexOutOfBoundsException e) {
    throw new IllegalArgumentException("Invalid index " + index, e); // boundary translates, does not swallow
}

Prevention

When it happens

Trigger: Calling entity.getItems().get(i) with i < 0: off-by-one index math on possibly-empty lists, (page-1)*size going negative for page 0, hand-rolled binary search returning -1, or loop bounds computed from another collection's size.

Common situations: Pagination/offset arithmetic that assumes a non-empty list; porting code that used a sentinel index; iterating with 'while (i > -1)' loops over lazy collections; UI input converted directly into an index.

Related errors


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/e86c829363b93cd2. Report an issue: GitHub.