{"record":{"id":"9404b1b41c64ee68","repo":"hibernate/hibernate-orm","slug":"a-cte-with-the-label-s-already-exists","errorCode":null,"errorMessage":"A CTE with the label %s already exists","messagePattern":"A CTE with the label (.+?) already exists","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/select/AbstractSqmSelectQuery.java","lineNumber":206,"sourceCode":"\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\tString.format(\n\t\t\t\t\t\t\t\"Illegal CTE name [%s]. Names must start with an alphabetic character!\",\n\t\t\t\t\t\t\tname\n\t\t\t\t\t)\n\t\t\t);\n\t\t}\n\t\treturn name;\n\t}\n\n\tprotected <X> JpaCteCriteria<X> withInternal(String name, AbstractQuery<X> criteria) {\n\t\tfinal var cteStatement = new SqmCteStatement<>(\n\t\t\t\tname,\n\t\t\t\t(SqmSelectQuery<X>) criteria,\n\t\t\t\tthis,\n\t\t\t\tnodeBuilder()\n\t\t);\n\t\tif ( cteStatements.putIfAbsent( name, cteStatement ) != null ) {\n\t\t\tthrow new IllegalArgumentException( \"A CTE with the label \" + cteStatement.getCteTable().getCteName() + \" already exists\" );\n\t\t}\n\t\treturn cteStatement;\n\t}\n\n\tprotected <X> JpaCteCriteria<X> withInternal(\n\t\t\tString name,\n\t\t\tAbstractQuery<X> baseCriteria,\n\t\t\tboolean unionDistinct,\n\t\t\tFunction<JpaCteCriteria<X>, AbstractQuery<X>> recursiveCriteriaProducer) {\n\t\tfinal var cteStatement = new SqmCteStatement<>(\n\t\t\t\tname,\n\t\t\t\t(SqmSelectQuery<X>) baseCriteria,\n\t\t\t\tunionDistinct,\n\t\t\t\trecursiveCriteriaProducer,\n\t\t\t\tthis,\n\t\t\t\tnodeBuilder()\n\t\t);\n\t\tif ( cteStatements.putIfAbsent( name, cteStatement ) != null ) {","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/select/AbstractSqmSelectQuery.java#L188-L224","documentation":"AbstractSqmSelectQuery.withInternal throws IllegalArgumentException when registering a non-recursive CTE whose name already exists in the same query — a `cteStatements.putIfAbsent(name, ...)` returning the previous entry means two CTEs would share one label, which is ambiguous SQL. Criteria CTEs are referenced by name later (query.from(cte)), so duplicates are rejected at registration rather than at SQL rendering.","triggerScenarios":"Criteria: calling `query.with( \"totals\", sub1 )` and later `query.with( \"totals\", sub2 )` on the same CriteriaQuery/CriteriaDefinition; loops that register CTEs from a list/map containing a repeated key; composing query fragments where each fragment adds its own \"data\" CTE into one target query.","commonSituations":"Query-composition frameworks that merge criteria fragments and naively merge their CTEs; refactoring one big CTE into two without renaming; copy-paste of a helper that always registers a CTE with the same fixed name; recursive refactoring that moves a shared CTE into the parent while leaving the original registration.","solutions":["Check first: `if (query.getCteCriteria(name) == null) query.with(name, sub); else reuse the existing JpaCteCriteria`","Give merged fragments unique CTE names (prefix per fragment: \"a_data\", \"b_data\")","Centralize CTE registration in one helper that deduplicates by name","For the recursive variants, remember the base registration already owns the name — never re-register it in the recursive producer"],"exampleFix":"// before\nquery.with( \"totals\", cb.createQuery(Double.class)... );\n// ... later, another fragment:\nquery.with( \"totals\", cb.createQuery(Double.class)... ); // IllegalArgumentException\n\n// after\nif ( query.getCteCriteria( \"totals\" ) == null ) {\n    query.with( \"totals\", cb.createQuery(Double.class)... );\n} // else: reuse query.getCteCriteria(\"totals\")","handlingStrategy":"validation","validationCode":"if (query.getCteCriteria(name) == null) {\n    query.with(name, sub);\n} else {\n    return query.getCteCriteria(name); // reuse existing\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Centralize CTE registration behind a deduplicating helper","Namespace CTE names per module/fragment","Never re-register a name; fetch it via getCteCriteria instead"],"tags":["hibernate","criteria-api","cte","duplicate","naming"],"backgroundTag":"duplicate-cte-name","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}