hibernate/hibernate-orm · error · IllegalArgumentException
A CTE with the label {} already exists
Error message
A CTE with the label {} already exists What it means
The CteContainer implementation keys its statements by the CTE table expression (the label) and registers them with putIfAbsent. addCteStatement throws IllegalArgumentException when the key already exists in the same CTE scope - i.e. two CTE statements declare the same label in one statement scope.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/sql/spi/BaseSqmToSqlAstConverter.java:9496
@Override
public Map<String, CteStatement> getCteStatements() {
return cteStatements;
}
@Override
public CteStatement getCteStatement(String cteLabel) {
final var cteStatement = cteStatements.get( cteLabel );
return cteStatement == null && parent != null
? parent.getCteStatement( cteLabel )
: cteStatement;
}
@Override
public void addCteStatement(CteStatement cteStatement) {
final String tableExpression = cteStatement.getCteTable().getTableExpression();
if ( cteStatements.putIfAbsent( tableExpression, cteStatement ) != null ) {
throw new IllegalArgumentException( "A CTE with the label " + tableExpression + " already exists" );
}
}
@Override
public Map<String, CteObject> getCteObjects() {
return cteObjects;
}
@Override
public CteObject getCteObject(String cteObjectName) {
final var cteObject = cteObjects.get( cteObjectName );
return cteObject == null && parent != null
? parent.getCteObject( cteObjectName )
: cteObject;
}
@Override
public void addCteObject(CteObject cteObject) {View on GitHub (pinned to fad1729dce)
Solutions
- Rename one of the duplicate CTE labels so each is unique within the statement
- Merge the two same-named CTEs if they are logically the same definition
- When building CTEs programmatically, derive names from a counter or the content to guarantee uniqueness
Example fix
// before with ids as (select 1), ids as (select 2) select * from ids // after with ids_a as (select 1), ids_b as (select 2) select * from ids_a
Defensive patterns
Strategy: validation
Validate before calling
// Before adding: check the label is free in this scope
if (java.util.Arrays.stream(existingContainer.getCteStatements().toArray())
.anyMatch(s -> ((SqmCteStatement<?>) s).getCteTable().getCteName().equals(label))) {
throw new IllegalArgumentException("CTE label '" + label + "' already exists");
}
existingContainer.addCteStatement(newStatement); Try / catch
try {
session.createQuery(hql).executeUpdate();
} catch (IllegalArgumentException e) {
if (e.getMessage() != null && e.getMessage().contains("already exists")) {
log.error("Duplicate CTE label in: {}", hql);
}
throw e;
} Prevention
- Generate CTE labels from a per-statement counter or content hash
- Lint with-clauses for duplicate labels before executing assembled HQL
- Share CTE definitions via constants, one declaration point per query
When it happens
Trigger: HQL 'with t as (...), t as (...) select ...'; criteria code calling with(...) twice with the same name on one query; merging subquery CTE containers that both declare the same label into one scope.
Common situations: Copy-paste of with-clause fragments; generated queries concatenating CTE blocks with a fixed label like 'data' or 'ids'; refactor of shared CTE snippets into one statement.
Related errors
- Couldn't generate CTE name for base name [%s] after %d tries
- Could not find CTE for name '" + cteName + "'!
- A CTE object with the name {} already exists
- A CTE with the label {} already exists
- Cte root does not have an entity type. Use getReferencedPath
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/c90fad11081b6714.
Report an issue: GitHub.