hibernate/hibernate-orm · error · ParameterLabelException

Gap between '?%s' and '?%s' in ordinal parameter labels [%s]

Error message

Gap between '?%s' and '?%s' in ordinal parameter labels [%s] (ordinal parameters must be labelled sequentially)

What it means

After confirming ordinal labels start at 1, verifyOrdinalParamLabels walks the sorted labels and requires each to be exactly lastPosition + 1. A skipped number (for example ?1 followed by ?3) throws ParameterLabelException and the message prints the full label set so the gap is easy to spot. Like the start-from-1 rule, it fires at query creation time.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/query/internal/ParameterMetadataImpl.java:146

			final List<Integer> sortedLabels = new ArrayList<>( labels );
			sortedLabels.sort( ComparableComparator.instance() );

			int lastPosition = -1;
			for ( Integer sortedPosition : sortedLabels ) {
				if ( lastPosition == -1 ) {
					if ( sortedPosition != 1 ) {
						throw new ParameterLabelException(
								String.format(
										Locale.ROOT,
										"Ordinal parameter labels start from '?%s' (ordinal parameters must be labelled from '?1')",
										sortedPosition
								)
						);
					}
				}
				else {
					if ( sortedPosition != lastPosition + 1 ) {
						throw new ParameterLabelException(
								String.format(
										Locale.ROOT,
										"Gap between '?%s' and '?%s' in ordinal parameter labels [%s] (ordinal parameters must be labelled sequentially)",
										lastPosition,
										sortedPosition,
										join( ",", sortedLabels.iterator() )
								)
						);
					}
				}
				lastPosition = sortedPosition;
			}
		}
	}

	@Override
	public Collection<QueryParameter<?>> getParameters() {
		return unmodifiableSet( queryParameters.keySet() );

View on GitHub (pinned to fad1729dce)

Solutions

  1. Renumber the remaining ordinal labels sequentially (1, 2, 3, ...) after deleting a parameter.
  2. Switch the query to named parameters to make renumbering a non-issue.
  3. Keep query strings in one place (constant or resource) instead of concatenating fragments with baked-in labels.

Example fix

// before
String hql = "from Person p where p.name = ?1 and p.age > ?3"; // gap: no ?2

// after
String hql = "from Person p where p.name = ?1 and p.age > ?2";
Defensive patterns

Strategy: validation

Validate before calling

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

Try / catch

try {
    em.createQuery(hql);
} catch (IllegalArgumentException e) {
    // message lists the full label set; renumber before retrying
}

Prevention

When it happens

Trigger: A query string such as "... where a = ?1 and b = ?3" - the ?2 predicate was removed but later labels were never shifted down. Any gap between consecutive ordinal labels in one HQL/JPQL query triggers it.

Common situations: Removing a WHERE fragment from a query but leaving the remaining ?N numbers unchanged; assembling query strings from fragments where each fragment carries its own labels; Hibernate 5 to 6/7 migration since 5 did not enforce sequential labels.

Related errors


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