{"record":{"id":"3a8b23017dd6828d","repo":"hibernate/hibernate-orm","slug":"different-cte-with-same-name-s-found-in-differe","errorCode":null,"errorMessage":"Different CTE with same name [%s] found in different set operands!","messagePattern":"Different CTE with same name \\[(.+?)\\] found in different set operands!","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java","lineNumber":820,"sourceCode":"\t\treturn new SqmSubQuery<>(\n\t\t\t\tparent,\n\t\t\t\tnew SqmQueryGroup<>( this, operator, queryParts ),\n\t\t\t\tresultType,\n\t\t\t\tcteStatements,\n\t\t\t\tthis\n\t\t);\n\t}\n\n\tprivate <T> void collectQueryPartsAndCtes(\n\t\t\tSqmSelectQuery<T> query,\n\t\t\tList<SqmQueryPart<T>> queryParts,\n\t\t\tMap<String, SqmCteStatement<?>> cteStatements) {\n\t\tqueryParts.add( query.getQueryPart() );\n\t\tfor ( var cteStatement : query.getCteStatements() ) {\n\t\t\tfinal String name = cteStatement.getCteTable().getCteName();\n\t\t\tfinal var old = cteStatements.put( name, cteStatement );\n\t\t\tif ( old != null && old != cteStatement ) {\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\tString.format( \"Different CTE with same name [%s] found in different set operands!\", name )\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic <X, T> SqmExpression<X> cast(JpaExpression<T> expression, Class<X> castTargetJavaType) {\n\t\treturn cast( expression, castTarget( castTargetJavaType ) );\n\t}\n\n\t@Override\n\tpublic <X, T> SqmExpression<X> cast(JpaExpression<T> expression, JpaCastTarget<X> castTarget) {\n\t\tfinal var sqmCastTarget = (SqmCastTarget<X>) castTarget;\n\t\treturn getFunctionDescriptor( \"cast\" ).generateSqmExpression(\n\t\t\t\tasList( (SqmTypedNode<?>) expression, sqmCastTarget ),\n\t\t\t\tsqmCastTarget.getType(),\n\t\t\t\tqueryEngine","sourceCodeStart":802,"sourceCodeEnd":838,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java#L802-L838","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Define the shared CTE once, on the outer/first operand only, and let later operands reference its name without redefining it.","If each operand needs its own CTE, give them distinct names (idsA, idsB).","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)."],"exampleFix":"// before\nJpaCriteriaQuery<Order> q1 = ...; q1.with(\"ids\", c -> c.select(orderRoot.get(\"id\")).where(...));\nJpaCriteriaQuery<Order> q2 = ...; q2.with(\"ids\", c -> c.select(otherRoot.get(\"id\")).where(...));\ncb.union(q1, q2); // Different CTE with same name [ids] found in different set operands!\n\n// after\nq2.with(\"ids2\", c -> c.select(otherRoot.get(\"id\")).where(...)); // unique name per operand\ncb.union(q1, q2);","handlingStrategy":"validation","validationCode":"// Hibernate API: JpaCriteriaQuery exposes its CTEs\nSet<String> used = new HashSet<>();\nfor (CriteriaQuery<?> q : List.of(q1, q2)) {\n    if (q instanceof JpaCriteriaQuery<?> jq) {\n        // collect names via with-clause bookkeeping in your builder; JpaCteCriteria names must be unique across operands\n    }\n}\n// simplest: track names yourself when attaching CTEs\nif (!used.add(cteName)) throw new IllegalArgumentException(\"CTE name reused across operands: \" + cteName);","typeGuard":null,"tryCatchPattern":"try {\n    cb.union(q1, q2);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"Different CTE with same name\")) { /* rename the second operand's CTE and retry */ }\n    else throw e;\n}","preventionTips":["Attach each shared CTE to exactly one operand (or to the outer query) instead of duplicating it everywhere.","Generate CTE names programmatically (prefix + counter) to avoid collisions when operands are built by loops.","Keep per-operand builder helpers from stamping standard-named CTEs onto every query."],"tags":["hibernate","criteria","cte","set-operations","name-collision"],"backgroundTag":"cte-name-collision","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}