{"record":{"id":"aa95a9556d440379","repo":"apache/druid","slug":"can-t-compact-segments-of-non-consecutive-rootpart","errorCode":null,"errorMessage":"Can't compact segments of non-consecutive rootPartition range. Missing partitionIds between [%s] and [%s]","messagePattern":"Can't compact segments of non-consecutive rootPartition range\\. Missing partitionIds between \\[(.+?)\\] and \\[(.+?)\\]","errorType":"exception","errorClass":"ISE","httpStatus":null,"severity":"error","filePath":"indexing-service/src/main/java/org/apache/druid/indexing/common/task/TaskLockHelper.java","lineNumber":289,"sourceCode":"    // sanity check\n    for (int i = 0; i < sortedSegments.size() - 1; i++) {\n      final DataSegment curSegment = sortedSegments.get(i);\n      final DataSegment nextSegment = sortedSegments.get(i + 1);\n      if (curSegment.getStartRootPartitionId() == nextSegment.getStartRootPartitionId()\n          && curSegment.getEndRootPartitionId() == nextSegment.getEndRootPartitionId()) {\n        // Input segments should have the same or consecutive rootPartition range\n        if (curSegment.getMinorVersion() != nextSegment.getMinorVersion()\n            || curSegment.getAtomicUpdateGroupSize() != nextSegment.getAtomicUpdateGroupSize()) {\n          throw new ISE(\n              \"segment[%s] and segment[%s] have the same rootPartitionRange, but different minorVersion or atomicUpdateGroupSize\",\n              curSegment,\n              nextSegment\n          );\n        }\n        atomicUpdateGroupSize++;\n      } else {\n        if (curSegment.getEndRootPartitionId() != nextSegment.getStartRootPartitionId()) {\n          throw new ISE(\n              \"Can't compact segments of non-consecutive rootPartition range. Missing partitionIds between [%s] and [%s]\",\n              curSegment.getEndRootPartitionId(),\n              nextSegment.getStartRootPartitionId()\n          );\n        }\n        if (atomicUpdateGroupSize != curSegment.getAtomicUpdateGroupSize()) {\n          throw new ISE(\n              \"All atomicUpdateGroup must be compacted together. Expected size[%s] but current size[%s]\",\n              curSegment.getAtomicUpdateGroupSize(),\n              atomicUpdateGroupSize\n          );\n        }\n        atomicUpdateGroupSize = 1;\n      }\n    }\n    if (atomicUpdateGroupSize != sortedSegments.get(sortedSegments.size() - 1).getAtomicUpdateGroupSize()) {\n      throw new ISE(\n          \"All atomicUpdateGroup must be compacted together. Expected size[%s] but current size[%s]\",","sourceCodeStart":271,"sourceCodeEnd":307,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/indexing-service/src/main/java/org/apache/druid/indexing/common/task/TaskLockHelper.java#L271-L307","documentation":"Thrown when, after sorting by rootPartitionId, two adjacent segments in the same interval neither share a rootPartitionRange nor have consecutive ranges: curSegment's endRootPartitionId is not nextSegment's startRootPartitionId minus gap-free continuity (they are not adjacent). Compaction/overwrite requires the input segments to cover a gap-free root-partition space so output segments can overshadow the entire range; holes mean some partitions are unaccounted for.","triggerScenarios":"verifyAndLockExistingSegments -> tryLockSegments -> verifyAndFindRootPartitionRangeAndMinorVersion with a segment list for one interval where some root partitions are missing (e.g. segments [0,3) and [5,8) supplied but [3,5) omitted).","commonSituations":"Manually filtered segment lists (excluding segments by version or by metadata predicate) that accidentally drop partitions; killing/tombstoning some partitions before compaction; partial failures in prior replace operations; querying only a subset of used segments.","solutions":["Include every used segment of the interval in the input list so the rootPartition ranges are consecutive with no gaps.","Check the segments table/segment metadata for missing partitionIds in the interval and restore them (unmark as overshadowed/tombstoned) or re-run ingestion to recreate them.","If partitions were intentionally dropped, run a full re-index (replace) of the interval instead of segment-level compaction.","Verify no filtering predicate (version, shardSpec, datasource version) is excluding segments before the helper call."],"exampleFix":"// before: filtered subset leaves a partition hole\nList<DataSegment> input = all.stream().filter(s -> !s.getVersion().equals(oldVersion)).collect(toList());\nverifyRootPartitionIsAdjacentAndAtomicUpdateGroupIsFull(sort(input)); // ISE: gap\n\n// after: use all used segments for the interval\nList<DataSegment> input = timeline.getUsedSegmentsForInterval(ds, interval);\nverifyRootPartitionIsAdjacentAndAtomicUpdateGroupIsFull(sort(input));","handlingStrategy":"validation","validationCode":"// Java: ensure rootPartition ranges are gap-free before invoking compaction\nList<DataSegment> sorted = segments.stream()\n    .sorted(Comparator.comparingInt(DataSegment::getStartRootPartitionId))\n    .collect(Collectors.toList());\nfor (int i = 0; i < sorted.size() - 1; i++) {\n  if (sorted.get(i).getEndRootPartitionId() != sorted.get(i + 1).getStartRootPartitionId()\n      && sorted.get(i).getStartRootPartitionId() != sorted.get(i + 1).getStartRootPartitionId()) {\n    throw new IllegalStateException(\"Gap in rootPartition range between index \" + i + \" and \" + (i + 1));\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  TaskLockHelper.verifyRootPartitionIsAdjacentAndAtomicUpdateGroupIsFull(sorted);\n} catch (IllegalStateException e) {\n  if (e.getMessage().contains(\"non-consecutive rootPartition range\")) {\n    // fetch the full used-segment set for the interval and retry\n  } else { throw e; }\n}","preventionTips":["Always build compaction input from the full used-segment timeline for the interval, not from filtered lists.","Avoid dropping/tombstoning individual partitions before compaction.","After failed replaces, verify segment continuity with a segments-table query before compacting."],"tags":["druid","root-partition","compaction","segment-lock"],"backgroundTag":"internal-invariant-violation","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}