apache/druid · error · UnsupportedOperationException

Limit push down when sorting by a post aggregator is not…

Error message

Limit push down when sorting by a post aggregator is not supported.

What it means

When forcing limit push down, GroupByQuery does not support an orderBy column that refers to a post-aggregator. The sort column must be a dimension or aggregator output; sorting by a post-aggregation value cannot be pushed into the group-by engine, so an UnsupportedOperationException is thrown.

Solutions

  1. Remove the post-aggregator from the ORDER BY / limit spec, sorting only by dimensions or aggregators
  2. Convert the post-aggregator expression into an actual aggregator where possible
  3. Disable limit push down (applyLimitPushDown=false) so the limit is applied after post-aggregation

Example fix

// before
.limit(new DefaultLimitSpec(ImmutableList.of(new OrderByColumnSpec("ratio", ASC)), 10)) // 'ratio' is a post-agg
// after
.limit(new DefaultLimitSpec(ImmutableList.of(new OrderByColumnSpec("count", ASC)), 10)) // sort by aggregator
Defensive patterns

Strategy: validation

Validate before calling

boolean sortsByPostAgg = ((DefaultLimitSpec) q.getLimitSpec()).getColumns().stream().anyMatch(c -> OrderByColumnSpec.getPostAggIndexForOrderBy(c, q.getPostAggregatorSpecs()) > -1);

Type guard

boolean limitColumnsAreNotPostAggs(GroupByQuery q) { return q.getLimitSpec() instanceof DefaultLimitSpec && ((DefaultLimitSpec) q.getLimitSpec()).getColumns().stream().noneMatch(c -> OrderByColumnSpec.getPostAggIndexForOrderBy(c, q.getPostAggregatorSpecs()) > -1); }

Try / catch

try { validateAndGetForceLimitPushDown(query, forcePushDown); } catch (UnsupportedOperationException e) { return false; }

Prevention

When it happens

Trigger: Calling GroupByQuery.isApplyLimitPushDown / validateAndGetForceLimitPushDown with a DefaultLimitSpec whose OrderByColumnSpec matches (via getPostAggIndexForOrderBy) one of the query's postAggregatorSpecs while forcing push down.

Common situations: SQL ORDER BY on an expression computed from aggregations translated into a post-aggregator sort with limit push down enabled; hand-built native queries sorting on ratio/computed columns with limits.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at processing/src/main/java/org/apache/druid/query/groupby/GroupByQuery.java:519

  private boolean validateAndGetForceLimitPushDown()
  {
    final boolean forcePushDown = context().getBoolean(GroupByQueryConfig.CTX_KEY_FORCE_LIMIT_PUSH_DOWN, false);
    if (forcePushDown) {
      if (!(limitSpec instanceof DefaultLimitSpec)) {
        throw new IAE("When forcing limit push down, a limit spec must be provided.");
      }

      if (!((DefaultLimitSpec) limitSpec).isLimited()) {
        throw new IAE("When forcing limit push down, the provided limit spec must have a limit.");
      }

      if (havingSpec != null) {
        throw new IAE("Cannot force limit push down when a having spec is present.");
      }

      for (OrderByColumnSpec orderBySpec : ((DefaultLimitSpec) limitSpec).getColumns()) {
        if (OrderByColumnSpec.getPostAggIndexForOrderBy(orderBySpec, postAggregatorSpecs) > -1) {
          throw new UnsupportedOperationException("Limit push down when sorting by a post aggregator is not supported.");
        }
      }
    }
    return forcePushDown;
  }

  private RowSignature computeResultRowSignature(final RowSignature.Finalization finalization)
  {
    final RowSignature.Builder builder = RowSignature.builder();

    if (universalTimestamp == null) {
      builder.addTimeColumn();
    }

    return builder.addDimensions(dimensions)
                  .addAggregators(aggregatorSpecs, finalization)
                  .addPostAggregators(postAggregatorSpecs)
                  .build();

View on GitHub (pinned to 9b90983fd2)