{"record":{"id":"15bfb6329eb35996","repo":"hibernate/hibernate-orm","slug":"ordinal-parameter-labels-start-from-s-ordinal","errorCode":null,"errorMessage":"Ordinal parameter labels start from '?%s' (ordinal parameters must be labelled from '?1')","messagePattern":"Ordinal parameter labels start from '\\?(.+?)' \\(ordinal parameters must be labelled from '\\?1'\\)","errorType":"exception","errorClass":"ParameterLabelException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/internal/ParameterMetadataImpl.java","lineNumber":135,"sourceCode":"\t\t\t\tqueryParameters.put( queryParameter, emptyList() );\n\t\t\t\ttempQueryParametersByName.put( queryParameter.getName(), queryParameter );\n\t\t\t}\n\t\t}\n\t\tqueryParametersByPosition = tempQueryParametersByPosition;\n\t\tqueryParametersByName = tempQueryParametersByName;\n\t\tqueryParameterBindingsTemplate = QueryParameterBindingsImpl.from( this, null );\n\t}\n\n\tprivate static void verifyOrdinalParamLabels(Set<Integer> labels) {\n\t\tif ( !isEmpty( labels ) ) {\n\t\t\tfinal List<Integer> sortedLabels = new ArrayList<>( labels );\n\t\t\tsortedLabels.sort( ComparableComparator.instance() );\n\n\t\t\tint lastPosition = -1;\n\t\t\tfor ( Integer sortedPosition : sortedLabels ) {\n\t\t\t\tif ( lastPosition == -1 ) {\n\t\t\t\t\tif ( sortedPosition != 1 ) {\n\t\t\t\t\t\tthrow new ParameterLabelException(\n\t\t\t\t\t\t\t\tString.format(\n\t\t\t\t\t\t\t\t\t\tLocale.ROOT,\n\t\t\t\t\t\t\t\t\t\t\"Ordinal parameter labels start from '?%s' (ordinal parameters must be labelled from '?1')\",\n\t\t\t\t\t\t\t\t\t\tsortedPosition\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif ( sortedPosition != lastPosition + 1 ) {\n\t\t\t\t\t\tthrow new ParameterLabelException(\n\t\t\t\t\t\t\t\tString.format(\n\t\t\t\t\t\t\t\t\t\tLocale.ROOT,\n\t\t\t\t\t\t\t\t\t\t\"Gap between '?%s' and '?%s' in ordinal parameter labels [%s] (ordinal parameters must be labelled sequentially)\",\n\t\t\t\t\t\t\t\t\t\tlastPosition,\n\t\t\t\t\t\t\t\t\t\tsortedPosition,\n\t\t\t\t\t\t\t\t\t\tjoin( \",\", sortedLabels.iterator() )\n\t\t\t\t\t\t\t\t)","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/internal/ParameterMetadataImpl.java#L117-L153","documentation":"When Hibernate parses HQL/JPQL it validates that ordinal parameter labels are consecutive integers starting at 1. ParameterMetadataImpl.verifyOrdinalParamLabels sorts the labels and throws ParameterLabelException at query creation when the smallest label is not 1. This matches the JPA spec and is stricter than Hibernate 5, which tolerated arbitrary positional labels.","triggerScenarios":"Creating a query whose first (lowest) ordinal label is greater than 1, e.g. createQuery(\"... where p.age > ?2\") or one starting at ?3. The exception is thrown while building ParameterMetadata, before any parameter is bound or the query executes.","commonSituations":"Upgrading Hibernate 5 to 6/7 where legacy queries kept arbitrary ?N numbers; deleting the first parameter from a query string and leaving later labels unshifted; copying numbered placeholders from SQL logs back into HQL.","solutions":["Renumber the ordinal labels in the query string so they start at ?1.","Prefer named parameters (:name) so labels can never go stale.","If many migrated queries are affected, add a startup or test-time check that parses each HQL string and validates the labels before first use."],"exampleFix":"// before\nList<Person> people = session\n    .createQuery(\"from Person p where p.age > ?2\", Person.class)\n    .list(); // ParameterLabelException: labels must start from '?1'\n\n// after\nList<Person> people = session\n    .createQuery(\"from Person p where p.age > ?1\", Person.class)\n    .list();","handlingStrategy":"validation","validationCode":"static void checkOrdinalLabels(String hql) {\n    java.util.regex.Matcher m = java.util.regex.Pattern.compile(\"\\\\?(\\\\d+)\").matcher(hql);\n    java.util.TreeSet<Integer> labels = new java.util.TreeSet<>();\n    while (m.find()) labels.add(Integer.parseInt(m.group(1)));\n    int expected = 1;\n    for (int label : labels) {\n        if (label != expected) throw new IllegalArgumentException(\n            \"Ordinal labels must start at ?1 and be sequential; offending label ?\" + label);\n        expected++;\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    em.createQuery(hql);\n} catch (IllegalArgumentException e) { // ParameterLabelException extends it\n    // surface 'labels start from ?1' to the caller / fail the test\n}","preventionTips":["Prefer named parameters in HQL/JPQL.","Run a unit test that creates every application query at startup to catch label errors early.","When deleting a predicate, renumber all remaining ordinal labels in the same edit."],"tags":["hibernate","hql","jpql","positional-parameters","parameter-labels","migration"],"backgroundTag":"positional-parameter-numbering","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}