hibernate/hibernate-orm · error · IllegalStateException
Encountered 'subclass table index' [%s] was outside expected
Error message
Encountered 'subclass table index' [%s] was outside expected range ( [%s] < i < [%s] )
What it means
Thrown while Hibernate builds the persister for a JOINED inheritance hierarchy. associateSubclassNamesToSubclassTableIndex maps each subclass-owned table to a slot in subclassNamesBySubclassTable; the computed index (position in subclassTableNameClosure minus coreTableSpan) must fall within [0, mapping.length). This error means the resolved index fell outside that array, i.e. a table owned by a subclass collided with the core (root/superclass) tables or the table-span arithmetic is inconsistent. It is an internal mapping invariant violation that aborts SessionFactory construction.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java:617
associateSubclassNamesToSubclassTableIndex( tableName, classNames, mapping );
for ( var join : persistentClass.getJoins() ) {
final String secondaryTableName = join.getTable().getQualifiedName( context );
associateSubclassNamesToSubclassTableIndex( secondaryTableName, classNames, mapping );
}
}
private void associateSubclassNamesToSubclassTableIndex(
String tableName,
Set<String> classNames,
String[][] mapping) {
// find the table's entry in the subclassTableNameClosure array
boolean found = false;
for ( int i = 1; i < subclassTableNameClosure.length; i++ ) {
if ( subclassTableNameClosure[i].equals( tableName ) ) {
found = true;
final int index = i - coreTableSpan;
if ( index < 0 || index >= mapping.length ) {
throw new IllegalStateException(
String.format(
"Encountered 'subclass table index' [%s] was outside expected range ( [%s] < i < [%s] )",
index,
0,
mapping.length
)
);
}
mapping[index] = toStringArray( classNames );
break;
}
}
if ( !found ) {
throw new IllegalStateException(
String.format(
"Was unable to locate subclass table [%s] in 'subclassTableNameClosure'",
tableName
)View on GitHub (pinned to fad1729dce)
Solutions
- Make every table name in the JOINED hierarchy unique, including secondary tables and @JoinTable names
- Check @SecondaryTable/@Table/@JoinTable name collisions between root, superclass joins, and subclass joins (case-insensitive compare)
- Upgrade to the latest Hibernate patch release - invariant bugs around subclass table closure have been fixed across 6.x/7.x
- Reduce the mapping to a minimal reproducer and report it as a Hibernate (HHH) issue with the mapping
Example fix
// before: subclass join reuses the root table name
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Person { ... }
@Entity
@SecondaryTable(name = "person") // same as root table -> index collapses
public class Customer extends Person { ... }
// after: unique table name
@Entity
@SecondaryTable(name = "customer_details")
public class Customer extends Person { ... } Defensive patterns
Strategy: validation
Validate before calling
// after building Metadata, before SessionFactory: no duplicate table names per hierarchy
for (PersistentClass pc : metadata.getEntityBindings()) {
Set<String> names = new HashSet<>();
names.add(pc.getRootTable().getName().toLowerCase(Locale.ROOT));
pc.getJoinClosureIterator().forEachRemaining(j -> {
if (!names.add(j.getTable().getName().toLowerCase(Locale.ROOT))) {
throw new IllegalStateException("Duplicate table name in hierarchy of " + pc.getEntityName());
}
});
} Try / catch
try { sessionFactory = metadata.buildSessionFactory(); } catch (IllegalStateException e) { /* mapping invariant failure: fix hierarchy table names, fail fast in CI */ throw e; } Prevention
- Keep table names unique across an entire inheritance hierarchy, including secondary tables and join tables
- Add a bootstrap test that builds the SessionFactory so mapping invariant failures surface in CI, not production
- Run a duplicate-table-name scan (above) as part of build validation
When it happens
Trigger: SessionFactory boot with @Inheritance(JOINED) where a subclass table, secondary table, or @JoinTable reuses the name of the root table or a core superclass table; hbm.xml <join> mappings with duplicate table names across the hierarchy; rarely an internal Hibernate defect in coreTableSpan computation.
Common situations: Copy-pasted @Table(name=...) values across a hierarchy; @SecondaryTable/@JoinTable names colliding with core tables; migrating hbm.xml joins from Hibernate 5; upgrading between 6.x/7.x versions and hitting an edge case in subclass table closure construction.
Related errors
- Discriminator formulas on joined inheritance hierarchies not
- optimistic-lock=all|dirty not supported for joined-subclass
- Was unable to locate subclass table [%s] in 'subclassTableNa
- discriminator mapping required for single table polymorphic
- No audit mapping available for %s
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/7ac2367c8b6a8327.
Report an issue: GitHub.