hibernate/hibernate-orm · error · HibernateException

Illegal null value for list index encountered while reading:

Error message

Illegal null value for list index encountered while reading: 

What it means

Hibernate 6's ListInitializer reads one row at a time when materializing an ordered List mapping (@OneToMany/@ManyToMany/@ElementCollection on a List with @OrderColumn, or @ListIndex). For every row it assembles the index column value first; if the index column reads as NULL (listIndexAssembler.assemble() returns null), it throws HibernateException with 'Illegal null value for list index encountered while reading' plus the collection's navigable role. The index column is the single source of positional truth for a PersistentList, so a NULL leaves Hibernate unable to place the element.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/ListInitializer.java:88

	protected void forEachSubInitializer(BiConsumer<Initializer<?>, RowProcessingState> consumer, InitializerData data) {
		super.forEachSubInitializer( consumer, data );
		final var initializer = elementAssembler.getInitializer();
		if ( initializer != null ) {
			consumer.accept( initializer, data.getRowProcessingState() );
		}
	}

	@Override
	public @Nullable PersistentList<?> getCollectionInstance(ImmediateCollectionInitializerData data) {
		return (PersistentList<?>) super.getCollectionInstance( data );
	}

	@Override
	protected void readCollectionRow(ImmediateCollectionInitializerData data, List<Object> loadingState) {
		final var rowProcessingState = data.getRowProcessingState();
		final Integer indexValue = listIndexAssembler.assemble( rowProcessingState );
		if ( indexValue == null ) {
			throw new HibernateException( "Illegal null value for list index encountered while reading: "
					+ getCollectionAttributeMapping().getNavigableRole() );
		}
		final Object element = elementAssembler.assemble( rowProcessingState );
		if ( element != null ) {
			int index = indexValue;
			if ( listIndexBase != 0 ) {
				index -= listIndexBase;
			}
			for ( int i = loadingState.size(); i <= index; ++i ) {
				loadingState.add( i, null );
			}
			loadingState.set( index, element );
		}
		// else if the element is null, then NotFoundAction must be IGNORE
	}

	@Override
	protected void initializeSubInstancesFromParent(ImmediateCollectionInitializerData data) {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Backfill the NULL indexes, e.g. UPDATE child_table SET position_idx = ... WHERE position_idx IS NULL (use ROW_NUMBER() over a stable ordering).
  2. Add NOT NULL (and ideally UNIQUE(owner_id, position_idx)) constraints so the corruption cannot recur.
  3. If element order is not needed, drop @OrderColumn and map the collection as a bag (plain List without @OrderColumn) or switch to @OrderBy, which orders on read and needs no index column.
  4. Audit every write path (native queries, other services, triggers) that inserts into the collection table and make them maintain the index column.

Example fix

// before
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
@OrderColumn(name = "position_idx") // table contains NULL position_idx rows
private List<LineItem> items;

-- repair the data
UPDATE order_line_item SET position_idx = rn - 1
FROM (SELECT id, ROW_NUMBER() OVER (PARTITION BY order_id ORDER BY id) AS rn FROM order_line_item) s
WHERE order_line_item.id = s.id;

// after (if ordering is unimportant)
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<LineItem> items; // bag semantics, no index column read
Defensive patterns

Strategy: validation

Validate before calling

// Before reading entities with @OrderColumn collections, verify no NULL indexes exist:
String check = "select count(*) from order_line_item where position_idx is null";
Long nulls = (Long) em.createNativeQuery(check).getSingleResult();
if (nulls != null && nulls > 0) {
    throw new IllegalStateException(nulls + " collection rows have a NULL list index - run backfill before loading");
}

Prevention

When it happens

Trigger: Loading or fetching an entity whose List collection is mapped with @OrderColumn (or @ListIndex) when at least one collection row has NULL in the order/index column. Happens during query result processing (JPQL/HQL, criteria, or lazy collection initialization) as soon as the offending row is read. A non-zero @ListIndexBase does not help: the check is on the raw assembled value before listIndexBase is subtracted.

Common situations: Rows inserted by hand, ETL jobs, or another application that leave the order column NULL; adding an @OrderColumn to an existing table without backfilling existing rows; schema where the order column is nullable; native SQL inserts that bypass Hibernate's index maintenance.

Related errors


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