apache/druid · error · IllegalArgumentException

Cannot order by a non-numeric aggregator[%s]

Error message

Cannot order by a non-numeric aggregator[%s]

What it means

Thrown by bufferComparatorWithAggregators when an ORDER BY / limit column maps to an aggregator whose output value type is not numeric (e.g. STRING or COMPLEX). The group-by v2 engine's in-memory buffer comparator can only compare aggregator slots numerically, so a non-numeric aggregator in the ordering spec cannot be supported and the query is rejected at comparator construction time.

Source

Thrown at processing/src/main/java/org/apache/druid/query/groupby/epinephelinae/GrouperBufferComparatorUtils.java:158

    for (OrderByColumnSpec orderSpec : limitSpec.getColumns()) {
      needsReverse = orderSpec.getDirection() != OrderByColumnSpec.Direction.ASCENDING;
      int dimIndex = OrderByColumnSpec.getDimIndexForOrderBy(orderSpec, dimensions);
      if (dimIndex >= 0) {
        comparators.add(dimComparators[dimIndex]);
        orderByIndices.add(dimIndex);
        needsReverses.add(needsReverse);
      } else {
        int aggIndex = OrderByColumnSpec.getAggIndexForOrderBy(orderSpec, Arrays.asList(aggregatorFactories));
        if (aggIndex >= 0) {
          final StringComparator stringComparator = orderSpec.getDimensionComparator();
          final ColumnType valueType = aggregatorFactories[aggIndex].getIntermediateType();
          // Aggregators start after dimensions
          final int aggOffset = keySize + aggregatorOffsets[aggIndex];

          aggCount++;

          if (!valueType.isNumeric()) {
            throw new IAE("Cannot order by a non-numeric aggregator[%s]", orderSpec);
          }

          comparators.add(
              makeNullHandlingBufferComparatorForNumericData(
                  aggOffset,
                  makeNumericBufferComparator(valueType, aggOffset, true, stringComparator)
              )
          );
          needsReverses.add(needsReverse);
        }
      }
    }

    for (int i = 0; i < dimCount; i++) {
      if (!orderByIndices.contains(i)) {
        comparators.add(dimComparators[i]);
        needsReverses.add(false); // default to Ascending order if dim is not in an orderby spec
      }

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Order by a numeric aggregator output instead, or add a numeric aggregator (e.g. longSum/count) computed from the same column and order by that.
  2. If ordering by a string value is required, remove the sort column from the limit spec and sort client-side, or use a post-aggregator that yields a number.
  3. Cast/convert the string aggregator to numeric where semantically valid (e.g. parse the string to a number in a downstream step or via a numeric aggregator at ingestion).

Example fix

// before: orderByColumnSpecs: ["stringFirst(user)"] with aggregator stringFirst("user")
// after
{"type":"longSum","name":"userCount","fieldName":"events"}
// order by "userCount" instead of the string aggregator output
Defensive patterns

Strategy: validation

Validate before calling

for (String col : limitSpec.getColumns()) {
  String type = resolveOutputType(query, col.getDimension());
  if (type != null && !NUMERIC_TYPES.contains(type)) {
    throw new IllegalArgumentException("Order-by column '" + col.getDimension() + "' has non-numeric aggregator type " + type);
  }
}

Prevention

When it happens

Trigger: Building a Grouper buffer comparator with an OrderByColumnSpec whose column resolves to a non-numeric aggregator output type (aggregatorOffset lookup succeeds but valueType.isNumeric() is false), e.g. ordering by a 'stringFirst'/'stringLast'/filtered-string or sketch aggregator output in a groupBy query with sort/limit columns.

Common situations: Users add topN/limit orderBy on a stringFirst, stringLast, earliest/latest-by (string output), or HLL sketch aggregator; queries migrated from other engines where ordering by strings is allowed; auto-generated dashboards sorting by whatever column the user clicked.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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