hibernate/hibernate-orm · error · SemanticException

Numeric literal '${position}' used in 'group by' does not ma

Error message

Numeric literal '${position}' used in 'group by' does not match a registered select item

What it means

Thrown while Hibernate translates HQL: a positional reference in 'group by' (e.g. 'group by 2') was validated against the select list of the query part and no select item is registered at that 1-based position (nodeByPosition returned null). Hibernate must map the ordinal to an SqmAliasedNodeRef pointing at a real select item, so an out-of-range ordinal is a semantic error, not silently ignored.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java:1634

		final var processingState = processingStateStack.getCurrent();
		final var processingQuery = processingState.getProcessingQuery();
		final var queryPart = sqmQueryPart( processingQuery );
		if ( child instanceof TerminalNode ) {
			if ( definedCollate ) {
				// This is syntactically disallowed
				throw new SyntaxException( "'collate' is not allowed for position based 'order by' or 'group by' items" );
			}
			else if ( !allowPositionalOrAliases ) {
				// This is syntactically disallowed
				throw new SyntaxException( "Position based 'order by' is not allowed in 'over' or 'within group' clauses" );
			}

			final int position = Integer.parseInt( child.getText() );

			// make sure this selection exists
			final var nodeByPosition = nodeByPosition( queryPart, position, processingState );
			if ( nodeByPosition == null ) {
				throw new SemanticException( "Numeric literal '" + position
						+ "' used in 'group by' does not match a registered select item",
						query );
			}

			return new SqmAliasedNodeRef(
					position,
					nodeBuilder.resolveExpressible( integerDomainType ),
					nodeBuilder
			);
		}
		else if ( child instanceof HqlParser.IdentifierContext identifierContext ) {
			final String identifierText = visitIdentifier( identifierContext );
			if ( queryPart instanceof SqmQueryGroup<?> ) {
				// If the current query part is a query group, check if the text matches
				// an attribute name of one of the selected SqmFrom elements or the path source name of a SqmPath
				SqmFrom<?, ?> found = null;
				int sqmPosition = 0;
				final var selections = queryPart.getFirstQuerySpec().getSelectClause().getSelections();

View on GitHub (pinned to fad1729dce)

Solutions

  1. Replace the ordinal with the actual expression or path: 'group by e.name'
  2. If keeping ordinals, verify 1 <= n <= number of select items in that query part (positions are 1-based) and fix the index
  3. Alias the select item and group by the alias instead of the position
  4. For dynamic queries, build the select list first and derive ordinals from it, or always group by full expressions

Example fix

// before
select e.name, e.dept from Employee e group by 3

// after
select e.name, e.dept from Employee e group by e.dept
Defensive patterns

Strategy: validation

Validate before calling

static boolean validGroupByOrdinal(String hql, int n) {
    String lower = hql.toLowerCase();
    int s = lower.indexOf("select") + 6;
    int f = lower.indexOf(" from ");
    if (s < 6 || f < 0 || f <= s) return false;
    String list = hql.substring(s, f);
    if (list.isBlank()) return false;
    int depth = 0, count = 1;
    for (char c : list.toCharArray()) {
        if (c == '(') depth++;
        else if (c == ')') depth--;
        else if (c == ',' && depth == 0) count++;
    }
    return n >= 1 && n <= count;
}
// before: em.createQuery("select e.name from Employee e group by " + n)
if (!validGroupByOrdinal(hql, n)) throw new IllegalArgumentException("group-by ordinal out of range: " + n);

Try / catch

try {
    return session.createQuery(hql, Tuple.class).list();
} catch (org.hibernate.query.sqm.SemanticException e) {
    // UnknownEntityException also lands here (subclass)
    throw new IllegalArgumentException("HQL failed semantic validation: " + hql, e);
}

Prevention

When it happens

Trigger: HQL such as 'select e.name from Employee e group by 2' when only one select item exists; 'group by 0'; an ordinal greater than the select-list size; dynamically assembled queries where the 'group by N' was computed against a different select list (e.g. branches of a set operation with different select counts).

Common situations: Porting native SQL where GROUP BY 1 is legal into HQL; off-by-one mistakes from assuming 0-based positions; builders that append group-by ordinals before the select list is finalized.

Related errors


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