{"record":{"id":"2a07a7c831aea8eb","repo":"hibernate/hibernate-orm","slug":"illegal-cte-name-s-names-must-start-with-an-al-2a07a7","errorCode":null,"errorMessage":"Illegal CTE name [%s]. Names must start with an alphabetic character!","messagePattern":"Illegal CTE name \\[(.+?)\\]\\. Names must start with an alphabetic character!","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/select/AbstractSqmSelectQuery.java","lineNumber":188,"sourceCode":"\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\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\" );","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/select/AbstractSqmSelectQuery.java#L170-L206","documentation":"AbstractSqmSelectQuery.validateCteName throws IllegalArgumentException when the CTE name's first character is not alphabetic. SQL identifiers for common table expressions must start with a letter in Hibernate's criteria validation (digits, underscores, or symbols at position 0 are rejected), so names like \"1cte\", \"_tmp\", \"@x\", or names accidentally prefixed by a template marker fail here. The validation runs for every with/withRecursive* variant.","triggerScenarios":"Criteria `query.with( \"1_results\", sub )` or `query.with( \"_tmp\", sub )`; names assembled as prefix+counter where the prefix begins with a digit; sanitizing user input by stripping characters until the name starts with a digit/symbol; HQL-to-criteria translation tools carrying over quoted or decorated identifiers.","commonSituations":"Auto-generated CTE names (\"2024_report\", \"#1\") from reporting layers; i18n or non-ASCII names beginning with a localized character; underscores-first temporary-table naming conventions brought over from SQL dialect habits.","solutions":["Rename the CTE to start with an ASCII letter, e.g. \"cte1_results\" instead of \"1_results\"","Sanitize dynamic names: strip invalid prefixes and prepend a letter if needed before calling with()","Keep a naming utility that enforces [A-Za-z][A-Za-z0-9_]* for all query identifiers","Assert generated names in tests for every report/CTE source"],"exampleFix":"// before\nquery.with( \"1_results\", sub ); // IllegalArgumentException\n\n// after\nquery.with( \"results_1\", sub );","handlingStrategy":"validation","validationCode":"if (name == null || name.isEmpty() || !Character.isLetter(name.charAt(0))) {\n    throw new IllegalArgumentException(\"CTE name must start with a letter: \" + name);\n}\nquery.with(name, sub);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Enforce [A-Za-z][A-Za-z0-9_]* in your naming helper","Avoid digit or underscore prefixes when generating names","Test generated CTE names for every report source"],"tags":["hibernate","criteria-api","cte","naming","validation"],"backgroundTag":"query-identifier-validation","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}