apache/druid · error · IllegalStateException

Cannot create comparator for array type

Error message

Cannot create comparator for array type %s.

What it means

DefaultLimitSpec builds Comparators to apply a topN-style limit/order-by on group-by results. Comparator construction only supports scalar column types plus string-array and primitive-array types (compared via DimensionHandlerUtils.coerceToStringArray); for any other array type (e.g. COMPLEX arrays or otherwise unsupported array dimensions) it throws this ISE rather than producing a wrong ordering.

Solutions

  1. Change the query to order by a scalar column, or project/flatten the array into a scalar before applying the limit spec
  2. Remove or restrict the OrderByColumnSpec on the array column and sort client-side after the query returns
  3. If support is genuinely needed, extend dimensionOrdering to add a comparator branch for the missing array type and file an issue upstream

Example fix

// before
OrderByColumnSpec spec = OrderByColumnSpec.ascending("arrDim"); // arrDim is a complex array
new DefaultLimitSpec(ImmutableList.of(spec), 100)
// after
OrderByColumnSpec spec = OrderByColumnSpec.ascending("scalarDim"); // order by scalar instead
Defensive patterns

Strategy: validation

Validate before calling

ColumnCapabilities caps = rowSignature.getColumnCapabilities(colName);
if (caps != null && caps.getType().isArray() &&
    !(caps.getType() == ColumnType.STRING_ARRAY || caps.getType() == ColumnType.LONG_ARRAY || caps.getType() == ColumnType.DOUBLE_ARRAY)) {
  throw new IllegalArgumentException("Cannot order by array column of type " + caps.getType());
}

Type guard

boolean isOrderableArray(ColumnType t) {
  return t.isArray()
    && (ColumnType.STRING_ARRAY.equals(t) || ColumnType.LONG_ARRAY.equals(t) || ColumnType.DOUBLE_ARRAY.equals(t));
}

Try / catch

try {
  return limitSpec.build(...) ;
} catch (IllegalStateException e) {
  if (e.getMessage().startsWith("Cannot create comparator")) {
    // fall back to client-side sort or scalar projection
  } else throw e;
}

Prevention

When it happens

Trigger: Calling makeComparator/dimensionOrdering with an OrderByColumnSpec whose column has an array ColumnType not in the supported set (e.g. a complex or long/double array dimension ordering) while applying a DefaultLimitSpec to group-by results.

Common situations: Ordering group-by output by an array-typed dimension such as a filtered/transformed array column, or after upgrading Druid and introducing array-typed columns into queries that previously used scalar dimensions with limit specs.

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/a82412b1f518d6f5. Report an issue: GitHub.

Appendix: source

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

        };
      } else if (columnType.getElementType().equals(ColumnType.STRING)) {
        arrayComparator = (o1, o2) -> {
          if (stringComparator == null
              || StringComparators.NATURAL.equals(stringComparator)
              || StringComparators.LEXICOGRAPHIC.equals(stringComparator)) {
            return columnType.getNullableStrategy().compare(
                DimensionHandlerUtils.coerceToStringArray(o1),
                DimensionHandlerUtils.coerceToStringArray(o2)
            );
          }
          return new DimensionComparisonUtils.ArrayComparator<>(stringComparator)
              .compare(
                  DimensionHandlerUtils.coerceToStringArray(o1),
                  DimensionHandlerUtils.coerceToStringArray(o2)
              );
        };
      } else {
        throw new ISE("Cannot create comparator for array type %s.", columnType.toString());
      }
    }
    final Comparator comparatorToUse;
    if (arrayComparator != null) {
      comparatorToUse = arrayComparator;
    } else {
      comparatorToUse = DimensionComparisonUtils.isNaturalComparator(columnType.getType(), stringComparator)
                        ? columnType.getNullableStrategy()
                        : stringComparator;
    }

    return Ordering.from(
        Comparator.comparing(
            (ResultRow row) -> {
              if (columnType.isArray()) {
                // Arrays have a specialized comparator, that applies the ordering per element. That will handle the casting
                // and the comparison
                return row.get(column);

View on GitHub (pinned to 9b90983fd2)