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
- Change the ordinal to a value between 1 and the number of SELECT columns.
- Reference the column by name or expression instead of an ordinal.
- 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
- Prefer column names over ordinals in GROUP BY.
- Keep SELECT list and GROUP BY ordinals in sync when editing queries.
- Remember ordinals are 1-based in Presto.
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
- MUST_BE_AGGREGATE_OR_GROUP_BY
- TOO_MANY_GROUPING_SETS
- MUST_BE_COLUMN_REFERENCE
- INVALID_TABLE_PROPERTY
- Invalid time from server:
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/f2f8f1dbb1e0a497.
Report an issue: GitHub.