prestodb/presto · error · SemanticException

INVALID_ORDINAL

INVALID_ORDINAL

Error message

GROUP BY position %s is not in select list

What it means

When GROUP BY uses an ordinal (a GROUP BY 2 style reference), Presto validates the literal is within 1..N where N is the number of select-list expressions. An ordinal of 0, negative, or larger than the select list throws INVALID_ORDINAL. Ordinals must point at an existing SELECT output.

Source

Thrown at presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java:4383

        private List<Expression> analyzeGroupBy(QuerySpecification node, Scope scope, List<Expression> outputExpressions)
        {
            if (node.getGroupBy().isPresent()) {
                ImmutableList.Builder<Set<FieldId>> cubes = ImmutableList.builder();
                ImmutableList.Builder<List<FieldId>> rollups = ImmutableList.builder();
                ImmutableList.Builder<List<Set<FieldId>>> sets = ImmutableList.builder();
                ImmutableList.Builder<Expression> complexExpressions = ImmutableList.builder();
                ImmutableList.Builder<Expression> groupingExpressions = ImmutableList.builder();

                checkGroupingSetsCount(node.getGroupBy().get());
                for (GroupingElement groupingElement : node.getGroupBy().get().getGroupingElements()) {
                    if (groupingElement instanceof SimpleGroupBy) {
                        for (Expression column : groupingElement.getExpressions()) {
                            // simple GROUP BY expressions allow ordinals or arbitrary expressions
                            if (column instanceof LongLiteral) {
                                long ordinal = ((LongLiteral) column).getValue();
                                if (ordinal < 1 || ordinal > outputExpressions.size()) {
                                    throw new SemanticException(INVALID_ORDINAL, column, "GROUP BY position %s is not in select list", ordinal);
                                }

                                column = outputExpressions.get(toIntExact(ordinal - 1));
                            }
                            else {
                                analyzeExpression(column, scope);
                            }

                            if (analysis.getColumnReferenceFields().containsKey(NodeRef.of(column))) {
                                sets.add(ImmutableList.of(ImmutableSet.copyOf(analysis.getColumnReferenceFields().get(NodeRef.of(column)))));
                            }
                            else {
                                verifyNoAggregateWindowOrGroupingFunctions(analysis.getFunctionHandles(), functionAndTypeResolver, column, "GROUP BY clause");
                                analysis.recordSubqueries(node, analyzeExpression(column, scope));
                                complexExpressions.add(column);
                            }

                            groupingExpressions.add(column);

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Change the ordinal to a value between 1 and the number of SELECT columns.
  2. Reference the column by name or expression instead of an ordinal.
  3. If SQL is generated, validate ordinal values against the projection list before emitting the query.

Example fix

// before
SELECT a, b, count(*) FROM t GROUP BY 3;
// after
SELECT a, b, count(*) FROM t GROUP BY 1, 2;
Defensive patterns

Strategy: validation

Validate before calling

long selectCount = selectItems.size();
for (Expression g : groupByExpressions) {
    if (g instanceof LongLiteral) {
        long ordinal = ((LongLiteral) g).getValue();
        if (ordinal < 1 || ordinal > selectCount) {
            throw new IllegalArgumentException("GROUP BY ordinal " + ordinal + " out of range 1.." + selectCount);
        }
    }
}

Try / catch

try { execute(sql); } catch (SemanticException e) { if (e.getCode() == INVALID_ORDINAL) { /* regenerate SQL with corrected ordinals or names */ } else { throw e; } }

Prevention

When it happens

Trigger: GROUP BY 0, GROUP BY with a literal greater than the number of SELECT items (e.g. three select columns with GROUP BY 4), or programmatic SQL generation emitting an out-of-range ordinal.

Common situations: Hand-written or generated SQL where SELECT columns were added/removed without updating GROUP BY ordinals; confusion over 0-based vs 1-based indexing; ORMs reordering the projection.

Related errors


AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04). Data as JSON: /api/errors/f2f8f1dbb1e0a497. Report an issue: GitHub.