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
- Backfill the NULL indexes, e.g. UPDATE child_table SET position_idx = ... WHERE position_idx IS NULL (use ROW_NUMBER() over a stable ordering).
- Add NOT NULL (and ideally UNIQUE(owner_id, position_idx)) constraints so the corruption cannot recur.
- 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.
- 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
- Add NOT NULL + UNIQUE(owner_id, position_idx) constraints on every @OrderColumn column.
- Run the NULL-index check as part of CI data checks or a startup Flyway/verify migration.
- Never insert collection rows with native SQL without maintaining the order column.
- Cover collection loading with integration tests seeded with edge-case data.
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
- Could not locate table which owns column [%s] referenced in
- Could not find table group for: %s
- Entity `%s` with identifier value `%s` does not exist
- generic collections don't have indexes
- Duplicate collection definition '%s'
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/f6d504627be7c8df.
Report an issue: GitHub.