{"record":{"id":"b3a9557db244251d","repo":"hibernate/hibernate-orm","slug":"incoming-parameter-position-position-is-less-t","errorCode":null,"errorMessage":"Incoming parameter position [{position}] is less than base [1]","messagePattern":"Incoming parameter position \\[(.+?)\\] is less than base \\[1\\]","errorType":"exception","errorClass":"QueryException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/sql/internal/ParameterRecognizerImpl.java","lineNumber":168,"sourceCode":"\t\tif ( parameterList == null ) {\n\t\t\tparameterList = new ArrayList<>();\n\t\t}\n\n\t\tparameterList.add( new ParameterOccurrence( parameter, sqlStringBuffer.length() ) );\n\t\tsqlStringBuffer.append( \"?\" );\n\t}\n\n\t@Override\n\tpublic void jpaPositionalParameter(int position, int sourcePosition) {\n\t\tif ( parameterStyle == null ) {\n\t\t\tparameterStyle = ParameterStyle.NAMED;\n\t\t}\n\t\telse if ( parameterStyle != ParameterStyle.NAMED ) {\n\t\t\tthrow new ParameterRecognitionException( \"Cannot mix parameter styles between JDBC-style, ordinal and named in the same query\" );\n\t\t}\n\n\t\tif ( position < 1 ) {\n\t\t\tthrow new QueryException( \"Incoming parameter position [\" + position + \"] is less than base [1]\" );\n\t\t}\n\n\t\tQueryParameterImplementor<?> parameter = null;\n\n\t\tif ( positionalQueryParameters == null ) {\n\t\t\tpositionalQueryParameters = new HashMap<>();\n\t\t}\n\t\telse {\n\t\t\tparameter = positionalQueryParameters.get( position );\n\t\t}\n\n\t\tif ( parameter == null ) {\n\t\t\tparameter = QueryParameterPositionalImpl.fromNativeQuery( position );\n\t\t\tpositionalQueryParameters.put( position, parameter );\n\t\t}\n\n\t\tif ( parameterList == null ) {\n\t\t\tparameterList = new ArrayList<>();","sourceCodeStart":150,"sourceCodeEnd":186,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ParameterRecognizerImpl.java#L150-L186","documentation":"When ParameterRecognizerImpl recognizes a JPA ordinal parameter '?n', it rejects any position below the JPA base of 1 with a QueryException. In practice this catches '?0' (or programmatically supplied non-positive positions), since the parser only reaches this callback with a token of digits.","triggerScenarios":"A query string containing '?0', e.g. \"... where x = ?0\", parsed at query creation. Also possible when ordinal positions are generated programmatically from 0-based loop counters and injected into the SQL.","commonSituations":"Developers assuming 0-based parameter indexes (JDBC PreparedStatement uses 1-based too); code that builds labels from array indices; conversion from libraries where ?0 was tolerated.","solutions":["Change the first label to ?1 and shift the rest accordingly.","Fix the generator that produces labels from 0-based indices (use index + 1).","Prefer named parameters to remove off-by-one label bugs entirely."],"exampleFix":"-- before\nselect * from person where x = ?0\n\n-- after\nselect * from person where x = ?1","handlingStrategy":"validation","validationCode":"static void validateNoZeroLabels(String sql) {\n    var m = java.util.regex.Pattern.compile(\"\\\\?(\\\\d+)\").matcher(sql);\n    while (m.find()) {\n        int pos = Integer.parseInt(m.group(1));\n        if (pos < 1) throw new IllegalArgumentException(\"Label ?\" + pos + \" is below the JPA base of 1\");\n    }\n}","typeGuard":null,"tryCatchPattern":"try { em.createNativeQuery(sql); } catch (org.hibernate.QueryException e) { /* if 'less than base [1]' bump ?0 to ?1 and shift the rest */ throw e; }","preventionTips":["Generate ordinal labels from index + 1, never from raw array indices.","Cover generated SQL in unit tests that assert the smallest label is 1."],"tags":["hibernate","ordinal-parameter","positional-parameter","query-exception","off-by-one"],"backgroundTag":"jpa-ordinal-parameter-label-invalid","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}