hibernate/hibernate-orm · error · ParameterLabelException

Gap between '?{previous}' and '?{position}' in ordinal param

Error message

Gap between '?{previous}' and '?{position}' in ordinal parameter labels (ordinal parameters must be labelled sequentially)

What it means

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.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/query/sql/internal/ParameterRecognizerImpl.java:63

	@Override
	public void complete() {
		// validate the positions.  JPA says that these should start with 1 and
		// increment contiguously (no gaps)
		if ( positionalQueryParameters != null ) {
			final int[] positionsArray = positionalQueryParameters.keySet().stream().mapToInt( Integer::intValue ).toArray();
			Arrays.sort( positionsArray );
			int previous = 0;
			boolean first = true;
			for ( Integer position : positionsArray ) {
				if ( position != previous + 1 ) {
					if ( first ) {
						throw new ParameterLabelException(
								"Ordinal parameter labels start from '?" + position + "'"
										+ " (ordinal parameters must be labelled from '?1')"
						);
					}
					else {
						throw new ParameterLabelException(
								"Gap between '?" + previous + "' and '?" + position + "' in ordinal parameter labels"
										+ " (ordinal parameters must be labelled sequentially)"
						);
					}
				}
				first = false;
				previous = position;
			}
		}
	}

	public Map<String, QueryParameterImplementor<?>> getNamedQueryParameters() {
		return namedQueryParameters;
	}

	public Map<Integer, QueryParameterImplementor<?>> getPositionalQueryParameters() {
		return positionalQueryParameters;
	}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Renumber all labels contiguously 1..N in the final query string; renumber programmatically when assembling dynamic SQL.
  2. Use named parameters when fragments are combined dynamically — they are order- and gap-free.
  3. Wrap dynamic assembly with a helper that rewrites '?k' tokens to sequential values as fragments are appended.

Example fix

-- before
select * from person where a = ?1 and b = ?3

-- after
select * from person where a = ?1 and b = ?2
Defensive patterns

Strategy: validation

Validate before calling

static void validateOrdinalLabelsContiguous(String sql) {
    var labels = new java.util.TreeSet<Integer>();
    var m = java.util.regex.Pattern.compile("\\?(\\d+)").matcher(sql);
    while (m.find()) labels.add(Integer.parseInt(m.group(1)));
    int expected = 1;
    for (int l : labels) {
        if (l != expected) throw new IllegalArgumentException("Gap: expected ?" + expected + " but found ?" + l);
        expected++;
    }
}

Try / catch

try { em.createQuery(ql).getResultList(); } catch (org.hibernate.query.ParameterLabelException e) { /* fix the reported gap between ?i and ?j */ throw e; }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/589ef26e25f51258. Report an issue: GitHub.