apache/druid · error · IllegalArgumentException

Cannot mix sortable and unsortable key columns

Error message

Cannot mix sortable and unsortable key columns

What it means

ClusterBy requires all key columns to be uniformly sortable or uniformly non-sortable, because mixing them prevents a consistent key ordering for partitioning/shuffling. If the first column's order is sortable and any later column's is not (or vice versa), construction fails.

Source

Thrown at processing/src/main/java/org/apache/druid/frame/key/ClusterBy.java:73

  )
  {
    this.columns = Preconditions.checkNotNull(columns, "columns");
    this.bucketByCount = bucketByCount;

    if (bucketByCount < 0 || bucketByCount > columns.size()) {
      throw new IAE("Invalid bucketByCount [%d]", bucketByCount);
    }

    // Key must be 100% sortable or 100% nonsortable. If empty, call it sortable.
    boolean sortable = true;

    for (int i = 0; i < columns.size(); i++) {
      final KeyColumn column = columns.get(i);

      if (i == 0) {
        sortable = column.order().sortable();
      } else if (sortable != column.order().sortable()) {
        throw new IAE("Cannot mix sortable and unsortable key columns");
      }
    }

    this.sortable = sortable;
  }

  /**
   * Create an empty key.
   */
  public static ClusterBy none()
  {
    return new ClusterBy(Collections.emptyList(), 0);
  }

  /**
   * The columns that comprise this key, in order.
   */
  @JsonProperty

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Change the clustering expression so every column uses a sortable ordering (avoid complex/sketch-type columns in CLUSTERED BY).
  2. Split the clustering key so non-sortable columns are not included, or wrap them with a sortable expression (e.g. cast or stringify).
  3. Inspect each KeyColumn's order().sortable() before constructing ClusterBy programmatically.
  4. If this comes from a generated plan, check the SQL layer's column ordering resolution for the query.

Example fix

// before
// CLUSTERED BY region, APPROX_COUNT_DISTINCT_DS_HLL(user)  -> mixed sortability
// after
// CLUSTERED BY region, user  (use only sortable columns in the clustering key)
Defensive patterns

Strategy: validation

Validate before calling

boolean first = columns.get(0).order().sortable();
for (KeyColumn c : columns) {
  if (c.order().sortable() != first) {
    throw new IllegalArgumentException("Mixed sortable/unsortable clustering columns");
  }
}

Type guard

static boolean uniformlySortable(List<KeyColumn> columns) {
  if (columns.isEmpty()) return true;
  boolean s = columns.get(0).order().sortable();
  return columns.stream().allMatch(c -> c.order().sortable() == s);
}

Try / catch

try {
  new ClusterBy(columns, bucketByCount);
} catch (IllegalArgumentException e) {
  if (e.getMessage().contains("mix sortable")) {
    columns = dropUnsortableColumns(columns);
    new ClusterBy(columns, Math.min(bucketByCount, columns.size()));
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Building a ClusterBy from a column list where KeyColumn.order().sortable() differs between columns — e.g. clustering on a mix of standard comparable columns and non-sortable column types (like complex/sketch columns).

Common situations: MSQ CLUSTERED BY including a non-sortable expression or complex column alongside regular columns; user typos in SQL clustering expressions; new column types whose order is non-sortable being added to an existing clustering spec.

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