apache/druid · error · IllegalArgumentException

Partitions must all abut each other

Error message

Partitions must all abut each other

What it means

FrameChannelMerger optimizes merging by only inspecting the earliest and latest key of the supplied ClusterByPartitions. That shortcut is only correct if the partition ranges have no gaps between them, so the constructor verifies partitionsToUse.allAbutting() and throws IAE otherwise. Callers must supply contiguous partition bounds.

Source

Thrown at processing/src/main/java/org/apache/druid/frame/processor/FrameChannelMerger.java:123

      final WritableFrameChannel outputChannel,
      final FrameWriterFactory frameWriterFactory,
      final List<KeyColumn> sortKey,
      @Nullable final FrameCombiner combiner,
      @Nullable final ClusterByPartitions partitions,
      final long rowLimit
  )
  {
    if (inputChannels.isEmpty()) {
      throw new IAE("Must have at least one input channel");
    }

    final ClusterByPartitions partitionsToUse =
        partitions == null ? ClusterByPartitions.oneUniversalPartition() : partitions;

    if (!partitionsToUse.allAbutting()) {
      // To simplify merging logic, when frames we only look at the earliest and latest key in "partitions". To ensure
      // correctness, we need to verify that there are no gaps.
      throw new IAE("Partitions must all abut each other");
    }

    if (!sortKey.stream().allMatch(keyColumn -> keyColumn.order().sortable())) {
      throw new IAE("Key is not sortable");
    }

    this.inputChannels = inputChannels;
    this.outputChannel = outputChannel;
    this.frameReader = frameReader;
    this.frameWriterFactory = frameWriterFactory;
    this.sortKey = sortKey;
    this.partitions = partitionsToUse;
    this.rowLimit = rowLimit;
    this.currentFrames = new FramePlus[inputChannels.size()];
    this.remainingChannels = new IntAVLTreeSet(IntSets.fromTo(0, inputChannels.size()));
    this.tournamentTree = new TournamentTree(
        inputChannels.size(),
        (k1, k2) -> {

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Use partition bounds produced by the standard clustering/partitioning code (e.g. from a previous ClusterBy stage) so ranges are contiguous.
  2. Validate ClusterByPartitions.allAbutting() before constructing the merger and repair gaps or use the full set.
  3. If partitions came from a filtered set, merge adjacent ranges or reject the set instead of passing it to the merger.

Example fix

// before
FrameChannelMerger merger = new FrameChannelMerger(allocator, sortKey, channels, null, filteredPartitions, rowLimit);
// after
if (!filteredPartitions.allAbutting()) {
  throw new IllegalStateException("Partitions have gaps; cannot merge: " + filteredPartitions);
}
FrameChannelMerger merger = new FrameChannelMerger(allocator, sortKey, channels, null, filteredPartitions, rowLimit);
Defensive patterns

Strategy: validation

Validate before calling

if (partitions != null && !partitions.allAbutting()) {
  throw new IllegalArgumentException("Partitions have gaps: " + partitions);
}

Type guard

boolean mergeable(ClusterByPartitions p) { return p == null || p.allAbutting(); }

Try / catch

try {
  merger = new FrameChannelMerger(allocator, sortKey, channels, null, partitions, rowLimit);
} catch (IllegalArgumentException e) {
  // regenerate partition bounds contiguously
}

Prevention

When it happens

Trigger: Constructing a FrameChannelMerger with a ClusterByPartitions whose ranges do not abut, e.g. partitions [0,10) and [20,30) with a gap (10,20), typically from manually built partition bounds or bounds filtered after generation.

Common situations: Custom partition-bound construction in stage/worker assignment code; dropping intermediate partitions when a worker fails; passing user-derived partition boundaries that skip ranges.

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