{"record":{"id":"c282d620f4998b48","repo":"hibernate/hibernate-orm","slug":"illegal-empty-cte-name","errorCode":null,"errorMessage":"Illegal empty CTE name","messagePattern":"Illegal empty CTE name","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/AbstractSqmDmlStatement.java","lineNumber":156,"sourceCode":"\tpublic <X> JpaCteCriteria<X> withRecursiveUnionAll(\n\t\t\t@Nonnull String name,\n\t\t\t@Nonnull AbstractQuery<X> baseCriteria,\n\t\t\t@Nonnull Function<JpaCteCriteria<X>, AbstractQuery<X>> recursiveCriteriaProducer) {\n\t\treturn withInternal( validateCteName( name ), baseCriteria, false, recursiveCriteriaProducer );\n\t}\n\n\t@Nonnull\n\t@Override\n\tpublic <X> JpaCteCriteria<X> withRecursiveUnionDistinct(\n\t\t\t@Nonnull String name,\n\t\t\t@Nonnull AbstractQuery<X> baseCriteria,\n\t\t\t@Nonnull Function<JpaCteCriteria<X>, AbstractQuery<X>> recursiveCriteriaProducer) {\n\t\treturn withInternal( validateCteName( name ), baseCriteria, true, recursiveCriteriaProducer );\n\t}\n\n\tprivate String validateCteName(String name) {\n\t\tif ( name == null || name.isBlank() ) {\n\t\t\tthrow new IllegalArgumentException( \"Illegal empty CTE name\" );\n\t\t}\n\t\tif ( !isAlphabetic( name.charAt( 0 ) ) ) {\n\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\tprivate <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()","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/AbstractSqmDmlStatement.java#L138-L174","documentation":"Hibernate throws this IllegalArgumentException from AbstractSqmDmlStatement.validateCteName when a criteria DML statement (SqmInsertStatement/SqmUpdateStatement/SqmDeleteStatement) registers a named CTE with a null or blank label. It applies to the JpaCteContainer methods with(String, criteria), withRecursiveUnionAll(string, ...) and withRecursiveUnionDistinct(string, ...) on the DML statement. It is a fail-fast query-construction error; nothing is executed against the database.","triggerScenarios":"Calling criteriaUpdate.with(null, cteQuery), .with(\"   \", cteQuery) or .withRecursiveUnionAll(\"\", base, producer) on a criteria insert/update/delete statement. Any named with* overload whose name argument is null, empty or whitespace-only hits this branch (name == null || name.isBlank()).","commonSituations":"CTE label built from user input, a config key or a Map key that turns out empty; refactors that renamed or dropped the label constant; translating an HQL 'WITH x AS (...)' to criteria and forgetting to carry the name over.","solutions":["Pass a non-empty label that starts with an alphabetic character, e.g. with(\"ids_to_delete\", cteQuery).","If the name is dynamic, validate it (non-null, non-blank) before calling with().","If you do not care about the label, use the unnamed overloads with(criteria) / withRecursiveUnionAll(criteria, producer) which auto-generate a unique alias.","Keep CTE labels in shared constants so they are never built ad hoc."],"exampleFix":"// before\ndelete.with( cteName, idsCte ); // cteName is \"\" or null -> \"Illegal empty CTE name\"\n// after\ndelete.with( \"ids_to_delete\", idsCte );","handlingStrategy":"validation","validationCode":"static String cteName(String raw) {\n    if ( raw == null || raw.isBlank() ) {\n        throw new IllegalArgumentException( \"CTE label must be non-blank, got: \" + raw );\n    }\n    return raw;\n}\n// use: delete.with( cteName( label ), cte );","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep CTE labels in constants; never build them from unvalidated input.","Prefer the unnamed with(criteria)/withRecursiveUnionAll(criteria, producer) overloads when the label does not matter.","Assert label validity once at the boundary where dynamic names enter query-building code."],"tags":["hibernate","criteria-api","cte","dml","illegal-argument"],"backgroundTag":"blank-or-null-argument","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}