hibernate/hibernate-orm · error · IllegalStateException
There must be at least a single root table assignment
Error message
There must be at least a single root table assignment
What it means
CteInsertHandler requires at least one assignment into the entity's ROOT table. The check fires when the statement doesn't assign the id (assignsId == false), the id generator is not generated-on-execution (i.e., manually assigned ids), AND no assignments target the root table — leaving no valid root-table INSERT to emit. It's an IllegalStateException raised during query translation.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertHandler.java:718
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Add the root insert as cte
final String rootTableName = entityPersister.getTableName( 0 );
final TableReference rootTableReference = updatingTableGroup.getTableReference(
updatingTableGroup.getNavigablePath(),
rootTableName,
true
);
final Generator identifierGenerator = entityPersister.getGenerator();
final List<Map.Entry<List<CteColumn>, Assignment>> tableAssignments = assignmentsByTable.get( rootTableReference );
if ( !assignsId && ( tableAssignments == null || tableAssignments.isEmpty() )
&& !identifierGenerator.generatedOnExecution() ) {
throw new IllegalStateException( "There must be at least a single root table assignment" );
}
final ConflictClause conflictClause = sqmConverter.visitConflictClause( sqmStatement.getConflictClause() );
final int tableSpan = entityPersister.getTableSpan();
final List<CteColumn> keyCteColumns = queryCte.getCteTable().findCteColumns( entityPersister.getIdentifierMapping() );
for ( int tableIndex = 0; tableIndex < tableSpan; tableIndex++ ) {
final String tableExpression = entityPersister.getTableName( tableIndex );
final TableReference updatingTableReference = updatingTableGroup.getTableReference(
updatingTableGroup.getNavigablePath(),
tableExpression,
true
);
final List<Map.Entry<List<CteColumn>, Assignment>> assignmentList = assignmentsByTable.get( updatingTableReference );
final NamedTableReference dmlTableReference = resolveUnionTableReference(
updatingTableReference,
tableExpression
);View on GitHub (pinned to fad1729dce)
Solutions
- Add the id attribute to the target list: `insert into SubType(id, name)`
- If ids should be database/sequence generated, configure @GeneratedValue on the id
- Ensure at least one root-table attribute appears in the insert column list
Example fix
// before insert into Region(code, name) select c.code, c.name from Country c // after (id is manually assigned) insert into Region(id, code, name) select c.id, c.code, c.name from Country c
Defensive patterns
Strategy: validation
Validate before calling
// Before executing insert...select, require the id (or a root column) in the target list
EntityType<?> et = em.getMetamodel().entity(Region.class);
if (!assignsGeneratedId(Region.class)
&& !targetColumns.contains(et.getDeclaredId(targetIdType).getName())) {
throw new IllegalArgumentException(
"Insert target list must include the assigned id attribute");
} Try / catch
try {
em.createQuery(insertHql).executeUpdate();
} catch (IllegalStateException e) {
if (e.getMessage().contains("root table assignment")) {
throw new QueryBuildException("Add the id (or a root-table column) to the insert list", e);
}
throw e;
} Prevention
- For entities with manually assigned ids, always include the id attribute in insert...select
- Use @GeneratedValue when ids should come from a sequence/identity
- Codify insert target lists in one builder that validates them against the metamodel
When it happens
Trigger: `insert into SubType(name) select ...` where SubType has a hand-assigned @Id (no @GeneratedValue) and every listed attribute belongs to a subclass/secondary table; omitting the id attribute from the insert target column list of an entity with assigned identifiers.
Common situations: HQL insert...select on entities with manually assigned or client-provided ids where the author forgot the id column; joined-inheritance inserts listing only subclass fields; legacy @Id mappings without generators being used with bulk insert statements.
Related errors
- Couldn't infer conflict constraint columns
- Couldn't find conflict constraint column [${constraintColumn
- Assignment referred to column of a joined association: ${col
- Assignment referred to columns from multiple tables
- Operand of " + op.getOperatorSqlText() + " is of type '" + r
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/3a875e9c0cb4a1eb.
Report an issue: GitHub.