apache/druid · error · IllegalArgumentException (IAE)

Column [%s] from 'orderBy' must also appear in 'columns'.

Error message

Column [%s] from 'orderBy' must also appear in 'columns'.

What it means

ScanQuery validates at construction time that every column named in 'orderBy' also appears in the query's 'columns' list. Druid throws this IAE because ordering requires reading the column during scan, so ordering on an unselected column is invalid. The specific message is used when the user explicitly supplied the orderBy list (as opposed to the implicit time-ordering case).

Source

Thrown at processing/src/main/java/org/apache/druid/query/scan/ScanQuery.java:182

            "Inconsistent number of columns[%d] and columnTypes[%d] specified!",
            columns.size(),
            columnTypes.size()
        );
      }
    }

    final Pair<List<OrderBy>, Order> ordering = verifyAndReconcileOrdering(orderBysFromUser, orderFromUser);
    this.orderBys = Preconditions.checkNotNull(ordering.lhs);
    this.timeOrder = ordering.rhs;

    if (this.columns != null && this.columns.size() > 0) {
      // Validate orderBy. (Cannot validate when signature is empty, since that means "discover at runtime".)

      for (final OrderBy orderByColumn : this.orderBys) {
        if (!this.columns.contains(orderByColumn.getColumnName())) {
          // Error message depends on how the user originally specified ordering.
          if (orderBysFromUser != null) {
            throw new IAE("Column [%s] from 'orderBy' must also appear in 'columns'.", orderByColumn.getColumnName());
          } else {
            throw new IllegalArgumentException("The __time column must be selected if the results are time-ordered.");
          }
        }
      }
    }

    this.maxRowsQueuedForOrdering = validateAndGetMaxRowsQueuedForOrdering();
    this.maxSegmentPartitionsOrderedInMemory = validateAndGetMaxSegmentPartitionsOrderedInMemory();
  }

  /**
   * Verifies that the ordering of a query is solely determined by {@link #getTimeOrder()}. Required to actually
   * execute queries, because {@link #getOrderBys()} is not yet understood by the query engines.
   *
   * @throws IllegalStateException if the ordering is not solely determined by {@link #getTimeOrder()}
   */
  public static void verifyOrderByForNativeExecution(final ScanQuery query)

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Add the orderBy column name to the query's 'columns' array
  2. Remove the orderBy entry for the column not selected
  3. If ordering by __time implicitly, ensure __time is in columns or drop the ordering

Example fix

// before
{"queryType":"scan","columns":["country"],"orderBy":[{"columnName":"city","direction":"ascending"}]}
// after
{"queryType":"scan","columns":["country","city"],"orderBy":[{"columnName":"city","direction":"ascending"}]}
Defensive patterns

Strategy: validation

Validate before calling

final Set<String> selected = new HashSet<>(query.getColumns());
for (ScanQuery.OrderBy ob : query.getOrderBys()) {
  if (!selected.contains(ob.getColumnName())) {
    throw new IllegalArgumentException("orderBy column not in columns: " + ob.getColumnName());
  }
}

Type guard

boolean orderByCovered(ScanQuery q) {
  Set<String> cols = new HashSet<>(q.getColumns());
  return q.getOrderBys().stream().allMatch(o -> cols.contains(o.getColumnName()));
}

Prevention

When it happens

Trigger: Constructing a ScanQuery (or deserializing one from JSON) where query.getOrderBys() contains a column name absent from query.getColumns(); e.g. POSTing a scan query with columns:["a"] and orderBy:[{"columnName":"b"}].

Common situations: Hand-written JSON scan queries where orderBy was edited without updating columns; SQL-generated scan queries after a projection change; clients that auto-add ordering on a timestamp column not included in the projection.

Understand the failure class

Background: "Invalid query parameter" / "Failed to parse value of ...": fixing bad query string parameters across APIs — this error's family across 36 libraries.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/0fcf734d53f66656. Report an issue: GitHub.