{"record":{"id":"31b4099e21ed36b9","repo":"hibernate/hibernate-orm","slug":"invalid-xml-element-name-passed-to-xmlforest","errorCode":null,"errorMessage":"Invalid XML element name passed to 'xmlforest()': %s","messagePattern":"Invalid XML element name passed to 'xmlforest\\(\\)': (.+?)","errorType":"exception","errorClass":"FunctionArgumentException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/dialect/function/xml/XmlForestFunction.java","lineNumber":54,"sourceCode":"\t\t\t\tStandardArgumentsValidators.composite(\n\t\t\t\t\t\tStandardArgumentsValidators.min( 1 ),\n\t\t\t\t\t\tnew ArgumentsValidator() {\n\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\tpublic void validate(\n\t\t\t\t\t\t\t\t\tList<? extends SqmTypedNode<?>> arguments,\n\t\t\t\t\t\t\t\t\tString functionName,\n\t\t\t\t\t\t\t\t\tBindingContext bindingContext) {\n\t\t\t\t\t\t\t\tfor ( int i = 0; i < arguments.size(); i++ ) {\n\t\t\t\t\t\t\t\t\tif ( !( arguments.get( i ) instanceof SqmNamedExpression<?> namedExpression ) ) {\n\t\t\t\t\t\t\t\t\t\tthrow new FunctionArgumentException(\n\t\t\t\t\t\t\t\t\t\t\t\tString.format(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"Parameter %d of function 'xmlforest()' is not named\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ti\n\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tif ( !XmlHelper.isValidXmlName( namedExpression.getName() ) ) {\n\t\t\t\t\t\t\t\t\t\tthrow new FunctionArgumentException(\n\t\t\t\t\t\t\t\t\t\t\t\tString.format(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"Invalid XML element name passed to 'xmlforest()': %s\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnamedExpression.getName()\n\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\t\t\t\t),\n\t\t\t\tStandardFunctionReturnTypeResolvers.invariant(\n\t\t\t\t\t\ttypeConfiguration.getBasicTypeRegistry().resolve( String.class, SqlTypes.SQLXML )\n\t\t\t\t),\n\t\t\t\tnull\n\t\t);\n\t}\n","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/dialect/function/xml/XmlForestFunction.java#L36-L72","documentation":"Each argument of 'xmlforest()' becomes an XML element whose tag name is taken from the argument alias, so the alias itself must be a legal XML name. XmlForestFunction calls XmlHelper.isValidXmlName on every alias and throws FunctionArgumentException naming the offending value when the check fails. A valid name is non-empty, starts with a letter, '_' or ':', does not start with 'xml' (case-insensitive), and contains only letters, digits, '_', ':', '-', '.'.","triggerScenarios":"An aliased xmlforest argument whose alias is not a valid XML name: select xmlforest(p.name as \"xmlName\") (reserved xml prefix), as \"first name\" (space), as \"2ndPhone\" (starts with digit), or as \"id@db\" (illegal character '@'). The exception is raised during SQM validation, before SQL execution.","commonSituations":"Reusing database column labels or JSON property names as xmlforest aliases; mapping domain fields like 'xmlData' or '24hFlag' straight into generated HQL; migrating from xmlquery/xmltable code where such names were only string data, not tag names.","solutions":["Rewrite the offending alias to start with a letter or underscore and use only [A-Za-z0-9_:. -], e.g. as \"first_name\" instead of as \"first name\"","Rename aliases beginning with 'xml'/'XML' (reserved prefix) such as \"xmlId\" to \"dataId\"","Validate generated aliases with the same rules before assembling the HQL string","Move XML construction to Java code if the required tag names cannot be made legal"],"exampleFix":"// before\nselect xmlforest(p.name as \"first name\", p.id as \"xmlId\") from Person p\n\n// after\nselect xmlforest(p.name as \"first_name\", p.id as \"dataId\") from Person p","handlingStrategy":"validation","validationCode":"static boolean isValidXmlName(String name) {\n    if (name == null || name.isEmpty()\n            || !(Character.isLetter(name.charAt(0)) || name.charAt(0) == '_' || name.charAt(0) == ':')\n            || name.regionMatches(true, 0, \"xml\", 0, 3)) {\n        return false;\n    }\n    for (int i = 1; i < name.length(); i++) {\n        char c = name.charAt(i);\n        if (!(Character.isLetterOrDigit(c) || c == '_' || c == ':' || c == '-' || c == '.')) {\n            return false;\n        }\n    }\n    return true;\n}\n\nif (aliases.stream().anyMatch(a -> !isValidXmlName(a))) throw new IllegalArgumentException(\"invalid xmlforest alias\");","typeGuard":null,"tryCatchPattern":"try {\n    return session.createQuery(hql, String.class).getSingleResult();\n}\ncatch (FunctionArgumentException e) {\n    // message names the invalid element name - map it to the offending alias in your query builder\n    throw new IllegalArgumentException(\"xmlforest() alias is not a valid XML element name: \" + e.getMessage(), e);\n}","preventionTips":["Derive xmlforest aliases from a whitelist of sanitized field names, not raw column/JSON labels","Sanitize: trim, replace spaces with '_', strip characters outside [A-Za-z0-9_.:-], ensure letter/underscore first char, and reject 'xml*' prefixes","Unit-test the alias sanitizer against your real field catalog"],"tags":["hibernate","hql","xml","query-validation","xmlforest"],"backgroundTag":"invalid-xml-name","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}