hibernate/hibernate-orm · error · IllegalArgumentException

Different CTE with same name [%s] found in different set ope

Error message

Different CTE with same name [%s] found in different set operands!

What it means

While flattening the operands of a criteria set operation, collectQueryPartsAndCtes merges each operand's CTE statements into one map keyed by CTE name. If the same CTE name was already contributed by a previous operand and the two SqmCteStatement objects are not the identical instance, Hibernate throws IllegalArgumentException, because the merged query could not decide which definition of the CTE to render.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java:820

		return new SqmSubQuery<>(
				parent,
				new SqmQueryGroup<>( this, operator, queryParts ),
				resultType,
				cteStatements,
				this
		);
	}

	private <T> void collectQueryPartsAndCtes(
			SqmSelectQuery<T> query,
			List<SqmQueryPart<T>> queryParts,
			Map<String, SqmCteStatement<?>> cteStatements) {
		queryParts.add( query.getQueryPart() );
		for ( var cteStatement : query.getCteStatements() ) {
			final String name = cteStatement.getCteTable().getCteName();
			final var old = cteStatements.put( name, cteStatement );
			if ( old != null && old != cteStatement ) {
				throw new IllegalArgumentException(
						String.format( "Different CTE with same name [%s] found in different set operands!", name )
				);
			}
		}
	}

	@Override
	public <X, T> SqmExpression<X> cast(JpaExpression<T> expression, Class<X> castTargetJavaType) {
		return cast( expression, castTarget( castTargetJavaType ) );
	}

	@Override
	public <X, T> SqmExpression<X> cast(JpaExpression<T> expression, JpaCastTarget<X> castTarget) {
		final var sqmCastTarget = (SqmCastTarget<X>) castTarget;
		return getFunctionDescriptor( "cast" ).generateSqmExpression(
				asList( (SqmTypedNode<?>) expression, sqmCastTarget ),
				sqmCastTarget.getType(),
				queryEngine

View on GitHub (pinned to fad1729dce)

Solutions

  1. Define the shared CTE once, on the outer/first operand only, and let later operands reference its name without redefining it.
  2. If each operand needs its own CTE, give them distinct names (idsA, idsB).
  3. Restructure so the CTE wraps the set operation instead of being duplicated inside it (a single outer query carrying the with-clause whose query part is the union group).

Example fix

// before
JpaCriteriaQuery<Order> q1 = ...; q1.with("ids", c -> c.select(orderRoot.get("id")).where(...));
JpaCriteriaQuery<Order> q2 = ...; q2.with("ids", c -> c.select(otherRoot.get("id")).where(...));
cb.union(q1, q2); // Different CTE with same name [ids] found in different set operands!

// after
q2.with("ids2", c -> c.select(otherRoot.get("id")).where(...)); // unique name per operand
cb.union(q1, q2);
Defensive patterns

Strategy: validation

Validate before calling

// Hibernate API: JpaCriteriaQuery exposes its CTEs
Set<String> used = new HashSet<>();
for (CriteriaQuery<?> q : List.of(q1, q2)) {
    if (q instanceof JpaCriteriaQuery<?> jq) {
        // collect names via with-clause bookkeeping in your builder; JpaCteCriteria names must be unique across operands
    }
}
// simplest: track names yourself when attaching CTEs
if (!used.add(cteName)) throw new IllegalArgumentException("CTE name reused across operands: " + cteName);

Try / catch

try {
    cb.union(q1, q2);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("Different CTE with same name")) { /* rename the second operand's CTE and retry */ }
    else throw e;
}

Prevention

When it happens

Trigger: cb.union(q1, q2) where both q1 and q2 define their own 'with ids as (...)' CTE via the JpaCriteriaQuery with()/withRecursive() API; identical-looking CTE definitions created separately in each operand (equal text but different SqmCteStatement instances still fails, since the check is old != cteStatement).

Common situations: Per-operand query builders (one per shard/table) that each attach the same-named CTE for filtering; migrating an HQL 'with ... union' query to the criteria API and copying the with-clause into every operand; helper factories that stamp a standard CTE (e.g. 'filtered') onto every query they build.

Related errors


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/3a8b23017dd6828d. Report an issue: GitHub.