{"record":{"id":"589ef26e25f51258","repo":"hibernate/hibernate-orm","slug":"gap-between-previous-and-position-in-ord","errorCode":null,"errorMessage":"Gap between '?{previous}' and '?{position}' in ordinal parameter labels (ordinal parameters must be labelled sequentially)","messagePattern":"Gap between '\\?(.+?)' and '\\?(.+?)' in ordinal parameter labels \\(ordinal parameters must be labelled sequentially\\)","errorType":"exception","errorClass":"ParameterLabelException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/sql/internal/ParameterRecognizerImpl.java","lineNumber":63,"sourceCode":"\t@Override\n\tpublic void complete() {\n\t\t// validate the positions.  JPA says that these should start with 1 and\n\t\t// increment contiguously (no gaps)\n\t\tif ( positionalQueryParameters != null ) {\n\t\t\tfinal int[] positionsArray = positionalQueryParameters.keySet().stream().mapToInt( Integer::intValue ).toArray();\n\t\t\tArrays.sort( positionsArray );\n\t\t\tint previous = 0;\n\t\t\tboolean first = true;\n\t\t\tfor ( Integer position : positionsArray ) {\n\t\t\t\tif ( position != previous + 1 ) {\n\t\t\t\t\tif ( first ) {\n\t\t\t\t\t\tthrow new ParameterLabelException(\n\t\t\t\t\t\t\t\t\"Ordinal parameter labels start from '?\" + position + \"'\"\n\t\t\t\t\t\t\t\t\t\t+ \" (ordinal parameters must be labelled from '?1')\"\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tthrow new ParameterLabelException(\n\t\t\t\t\t\t\t\t\"Gap between '?\" + previous + \"' and '?\" + position + \"' in ordinal parameter labels\"\n\t\t\t\t\t\t\t\t\t\t+ \" (ordinal parameters must be labelled sequentially)\"\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfirst = false;\n\t\t\t\tprevious = position;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic Map<String, QueryParameterImplementor<?>> getNamedQueryParameters() {\n\t\treturn namedQueryParameters;\n\t}\n\n\tpublic Map<Integer, QueryParameterImplementor<?>> getPositionalQueryParameters() {\n\t\treturn positionalQueryParameters;\n\t}","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ParameterRecognizerImpl.java#L45-L81","documentation":"The complement of the first-label check: ParameterRecognizerImpl.complete() walks the sorted ordinal labels and requires each to equal previous + 1. When a later label skips a number (e.g. ?1 then ?3) it throws ParameterLabelException reporting the gap — JPA demands strictly sequential labels.","triggerScenarios":"A query containing labels like ?1 and ?3 with no ?2 anywhere in the string, e.g. \"... where a = ?1 or b = ?3\". The gap can also appear after string concatenation of dynamic filter fragments that each hard-code their own labels.","commonSituations":"Dynamically appended conditions where each fragment uses fixed labels; deleting a parameter from a query but not renumbering the rest; merging two queries that each used ?1, ?2 into one string without renumbering.","solutions":["Renumber all labels contiguously 1..N in the final query string; renumber programmatically when assembling dynamic SQL.","Use named parameters when fragments are combined dynamically — they are order- and gap-free.","Wrap dynamic assembly with a helper that rewrites '?k' tokens to sequential values as fragments are appended."],"exampleFix":"-- before\nselect * from person where a = ?1 and b = ?3\n\n-- after\nselect * from person where a = ?1 and b = ?2","handlingStrategy":"validation","validationCode":"static void validateOrdinalLabelsContiguous(String sql) {\n    var labels = new java.util.TreeSet<Integer>();\n    var m = java.util.regex.Pattern.compile(\"\\\\?(\\\\d+)\").matcher(sql);\n    while (m.find()) labels.add(Integer.parseInt(m.group(1)));\n    int expected = 1;\n    for (int l : labels) {\n        if (l != expected) throw new IllegalArgumentException(\"Gap: expected ?\" + expected + \" but found ?\" + l);\n        expected++;\n    }\n}","typeGuard":null,"tryCatchPattern":"try { em.createQuery(ql).getResultList(); } catch (org.hibernate.query.ParameterLabelException e) { /* fix the reported gap between ?i and ?j */ throw e; }","preventionTips":["When deleting a parameter, renumber the remaining labels immediately.","Use named parameters for queries assembled from optional fragments."],"tags":["hibernate","ordinal-parameter","positional-parameter","parameter-label-exception","dynamic-sql"],"backgroundTag":"jpa-ordinal-parameter-label-invalid","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}