apache/druid · error · IllegalStateException

Unknown column in order clause[%s]

Error message

Unknown column in order clause[%s]

What it means

DefaultLimitSpec.getOrderByType resolves the output type of a column in the ORDER BY / limit clause by looking it up among the query's aggregators, post-aggregators, and dimension specs. If the ordering column matches none of them, an IllegalStateException is thrown — meaning the limit spec references a column the query does not actually produce.

Source

Thrown at processing/src/main/java/org/apache/druid/query/groupby/orderby/DefaultLimitSpec.java:294

    }

    // Finally, apply offset after sorting and limiting.
    if (isOffset()) {
      return results -> sortAndLimitFn.apply(results).skip(offset);
    } else {
      return sortAndLimitFn;
    }
  }

  private ColumnType getOrderByType(final OrderByColumnSpec columnSpec, final List<DimensionSpec> dimensions)
  {
    for (DimensionSpec dimSpec : dimensions) {
      if (columnSpec.getDimension().equals(dimSpec.getOutputName())) {
        return dimSpec.getOutputType();
      }
    }

    throw new ISE("Unknown column in order clause[%s]", columnSpec);
  }

  @Override
  public LimitSpec filterColumns(Set<String> names)
  {
    return new DefaultLimitSpec(
        columns.stream().filter(c -> names.contains(c.getDimension())).collect(Collectors.toList()),
        offset,
        limit
    );
  }

  /**
   * Returns a new DefaultLimitSpec identical to this one except for one difference: an offset parameter, if any, will
   * be removed and added to the limit. This is designed for passing down queries to lower levels of the stack. Only
   * the highest level should apply the offset parameter, and any pushed-down limits must be increased to accommodate
   * the offset.
   */

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Make the orderBy column name exactly match a dimension outputName, aggregator, or post-aggregator in the same query spec.
  2. Add the referenced column as a dimension/aggregator or remove it from the limit spec.
  3. If sorting should happen after an outer query, restructure with a nested query where each layer's limit spec references only its own outputs.

Example fix

// before
"limitSpec":{"type":"default","columns":[{"dimension":"user_name"}]}
// but dimension is {"dimension":"user","outputName":"user"}
// after
"limitSpec":{"type":"default","columns":[{"dimension":"user"}]}
Defensive patterns

Strategy: validation

Validate before calling

Set<String> outputs = new HashSet<>();
dimensions.forEach(d -> outputs.add(d.getOutputName()));
aggregators.forEach(a -> outputs.add(a.getName()));
postAggregators.forEach(p -> outputs.add(p.getName()));
for (OrderByColumnSpec c : limitSpec.getColumns()) {
  if (!outputs.contains(c.getDimension())) throw new IllegalArgumentException("Unknown order-by column: " + c.getDimension());
}

Try / catch

try {
  return engine.plan(query);
} catch (IllegalStateException e) {
  if (e.getMessage() != null && e.getMessage().contains("Unknown column in order clause")) {
    throw new QueryValidationException(e.getMessage(), e);
  }
  throw e;
}

Prevention

When it happens

Trigger: A DefaultLimitSpec (limit/orderBy in a groupBy or topN query) contains an OrderByColumnSpec whose name matches no dimension outputName, aggregator name, or post-aggregator name; getOrderByType is called during planning (e.g. via columnType).

Common situations: Typos in orderBy column names; dashboard/UI sending sort keys for columns removed from the query; renaming a dimension outputName without updating the limit spec; nested queries where the sort column only exists in the inner query.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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