apache/druid · error · IllegalArgumentException

Must provide at least one range

Error message

Must provide at least one range

What it means

ClusterByPartitions is a JSON-serialized list of ClusterByPartition ranges and must contain at least one range; an empty list is meaningless for partitioning. Its @JsonCreator constructor throws IAE when deserialized or constructed with an empty list.

Source

Thrown at processing/src/main/java/org/apache/druid/frame/key/ClusterByPartitions.java:49

/**
 * Holder object for a set of {@link ClusterByPartition}. There are no preconditions put upon the partitions, except
 * that there is at least one of them.
 *
 * In particular, they are not required to abut each other or to be non-overlapping. Use {@link #allAbutting()} to
 * check if this particular list of partitions is in fact all abutting (and, therefore, also non-overlapping).
 */
public class ClusterByPartitions implements Iterable<ClusterByPartition>
{
  private static final ClusterByPartitions ONE_UNIVERSAL_PARTITION =
      new ClusterByPartitions(Collections.singletonList(new ClusterByPartition(null, null)));

  private final List<ClusterByPartition> ranges;

  @JsonCreator
  public ClusterByPartitions(final List<ClusterByPartition> ranges)
  {
    if (ranges.isEmpty()) {
      throw new IAE("Must provide at least one range");
    }

    this.ranges = ranges;
  }

  public static ClusterByPartitions oneUniversalPartition()
  {
    return ONE_UNIVERSAL_PARTITION;
  }

  /**
   * Whether this list of partitions is all abutting, meaning: each partition's start is equal to the previous
   * partition's end.
   *
   * Note that the start of the first partition, and the end of the last partition, may or may not be unbounded.
   * So this list of partitions may not cover the entire space even if they are all abutting.
   */
  public boolean allAbutting()

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Provide at least one ClusterByPartition range in the spec, or use ClusterByPartitions.oneUniversalPartition() when a single all-encompassing partition is desired.
  2. Fix the upstream logic that generated the empty partitions list (it should fall back to a universal partition).
  3. Validate the JSON spec before submission: partitions array must be non-empty.
  4. If partitions are computed dynamically, guard: use oneUniversalPartition() when the computed list is empty.

Example fix

// before
new ClusterByPartitions(Collections.emptyList()); // IAE
// after
List<ClusterByPartition> ranges = computePartitions();
ClusterByPartitions p = ranges.isEmpty()
    ? ClusterByPartitions.oneUniversalPartition()
    : new ClusterByPartitions(ranges);
Defensive patterns

Strategy: validation

Validate before calling

if (ranges == null || ranges.isEmpty()) {
  ranges = List.of(ClusterByPartition.universal()); // or oneUniversalPartition()
}

Type guard

static boolean hasAtLeastOneRange(ClusterByPartitions p) {
  return p != null && !p.ranges.isEmpty();
}

Try / catch

try {
  objectMapper.readValue(specJson, ClusterByPartitions.class);
} catch (IllegalArgumentException e) {
  if (e.getMessage().contains("at least one range")) {
    return ClusterByPartitions.oneUniversalPartition();
  }
  throw e;
}

Prevention

When it happens

Trigger: Deserializing a clustering/partitioning spec from JSON where the partitions array is [], or calling the constructor with Collections.emptyList() — e.g. a partitioning pass that produced no target ranges.

Common situations: MSQ query specs hand-edited or generated with an empty targetPartitions array; a partitioning job wrote an empty result that is then fed back as config; client code building specs dynamically from an empty set of partitions.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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