{"record":{"id":"7ac2367c8b6a8327","repo":"hibernate/hibernate-orm","slug":"encountered-subclass-table-index-s-was-outsid","errorCode":null,"errorMessage":"Encountered 'subclass table index' [%s] was outside expected range ( [%s] < i < [%s] )","messagePattern":"Encountered 'subclass table index' \\[(.+?)\\] was outside expected range \\( \\[(.+?)\\] < i < \\[(.+?)\\] \\)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java","lineNumber":617,"sourceCode":"\t\tassociateSubclassNamesToSubclassTableIndex( tableName, classNames, mapping );\n\t\tfor ( var join : persistentClass.getJoins() ) {\n\t\t\tfinal String secondaryTableName = join.getTable().getQualifiedName( context );\n\t\t\tassociateSubclassNamesToSubclassTableIndex( secondaryTableName, classNames, mapping );\n\t\t}\n\t}\n\n\tprivate void associateSubclassNamesToSubclassTableIndex(\n\t\t\tString tableName,\n\t\t\tSet<String> classNames,\n\t\t\tString[][] mapping) {\n\t\t// find the table's entry in the subclassTableNameClosure array\n\t\tboolean found = false;\n\t\tfor ( int i = 1; i < subclassTableNameClosure.length; i++ ) {\n\t\t\tif ( subclassTableNameClosure[i].equals( tableName ) ) {\n\t\t\t\tfound = true;\n\t\t\t\tfinal int index = i - coreTableSpan;\n\t\t\t\tif ( index < 0 || index >= mapping.length ) {\n\t\t\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\t\tString.format(\n\t\t\t\t\t\t\t\t\t\"Encountered 'subclass table index' [%s] was outside expected range ( [%s] < i < [%s] )\",\n\t\t\t\t\t\t\t\t\tindex,\n\t\t\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t\t\tmapping.length\n\t\t\t\t\t\t\t)\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tmapping[index] = toStringArray( classNames );\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif ( !found ) {\n\t\t\tthrow new IllegalStateException(\n\t\t\t\t\tString.format(\n\t\t\t\t\t\t\t\"Was unable to locate subclass table [%s] in 'subclassTableNameClosure'\",\n\t\t\t\t\t\t\ttableName\n\t\t\t\t\t)","sourceCodeStart":599,"sourceCodeEnd":635,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java#L599-L635","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: subclass join reuses the root table name\n@Entity\n@Inheritance(strategy = InheritanceType.JOINED)\npublic class Person { ... }\n\n@Entity\n@SecondaryTable(name = \"person\") // same as root table -> index collapses\npublic class Customer extends Person { ... }\n\n// after: unique table name\n@Entity\n@SecondaryTable(name = \"customer_details\")\npublic class Customer extends Person { ... }","handlingStrategy":"validation","validationCode":"// after building Metadata, before SessionFactory: no duplicate table names per hierarchy\nfor (PersistentClass pc : metadata.getEntityBindings()) {\n    Set<String> names = new HashSet<>();\n    names.add(pc.getRootTable().getName().toLowerCase(Locale.ROOT));\n    pc.getJoinClosureIterator().forEachRemaining(j -> {\n        if (!names.add(j.getTable().getName().toLowerCase(Locale.ROOT))) {\n            throw new IllegalStateException(\"Duplicate table name in hierarchy of \" + pc.getEntityName());\n        }\n    });\n}","typeGuard":null,"tryCatchPattern":"try { sessionFactory = metadata.buildSessionFactory(); } catch (IllegalStateException e) { /* mapping invariant failure: fix hierarchy table names, fail fast in CI */ throw e; }","preventionTips":["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"],"tags":["hibernate","orm","joined-inheritance","mapping","session-factory-boot"],"backgroundTag":"joined-inheritance-table-mapping-error","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}