{"record":{"id":"2e387c064fd26a8a","repo":"apache/druid","slug":"root-partition-range-d-d-of-new-segments-doesn","errorCode":null,"errorMessage":"Root partition range[%d, %d] of new segments doesn't match to root partition range[%d, %d] of old segments","messagePattern":"Root partition range\\[(.+?), (.+?)\\] of new segments doesn't match to root partition range\\[(.+?), (.+?)\\] of old segments","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"indexing-service/src/main/java/org/apache/druid/indexing/common/actions/SegmentTransactionalInsertAction.java","lineNumber":293,"sourceCode":"    oldSegmentsMap.values().forEach(TaskLockHelper::verifyRootPartitionIsAdjacentAndAtomicUpdateGroupIsFull);\n    newSegmentsMap.values().forEach(TaskLockHelper::verifyRootPartitionIsAdjacentAndAtomicUpdateGroupIsFull);\n\n    oldSegmentsMap.forEach((interval, oldSegmentsPerInterval) -> {\n      final List<DataSegment> newSegmentsPerInterval = Preconditions.checkNotNull(\n          newSegmentsMap.get(interval),\n          \"segments of interval[%s]\",\n          interval\n      );\n      // These lists are already sorted in groupSegmentsByIntervalAndSort().\n      final int oldStartRootPartitionId = oldSegmentsPerInterval.get(0).getStartRootPartitionId();\n      final int oldEndRootPartitionId = oldSegmentsPerInterval.get(oldSegmentsPerInterval.size() - 1)\n                                                              .getEndRootPartitionId();\n      final int newStartRootPartitionId = newSegmentsPerInterval.get(0).getStartRootPartitionId();\n      final int newEndRootPartitionId = newSegmentsPerInterval.get(newSegmentsPerInterval.size() - 1)\n                                                              .getEndRootPartitionId();\n\n      if (oldStartRootPartitionId != newStartRootPartitionId || oldEndRootPartitionId != newEndRootPartitionId) {\n        throw new ISE(\n            \"Root partition range[%d, %d] of new segments doesn't match to root partition range[%d, %d] of old segments\",\n            newStartRootPartitionId,\n            newEndRootPartitionId,\n            oldStartRootPartitionId,\n            oldEndRootPartitionId\n        );\n      }\n\n      newSegmentsPerInterval\n          .forEach(eachNewSegment -> oldSegmentsPerInterval\n              .forEach(eachOldSegment -> {\n                if (eachNewSegment.getMinorVersion() <= eachOldSegment.getMinorVersion()) {\n                  throw new ISE(\n                      \"New segment[%s] have a smaller minor version than old segment[%s]\",\n                      eachNewSegment,\n                      eachOldSegment\n                  );\n                }","sourceCodeStart":275,"sourceCodeEnd":311,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/indexing-service/src/main/java/org/apache/druid/indexing/common/actions/SegmentTransactionalInsertAction.java#L275-L311","documentation":"SegmentTransactionalInsertAction.checkWithSegmentLock() validates that when new segments replace old segments for an interval, the new segments' root partition range exactly matches the old ones'. Root partition ids define top-level chunk boundaries in the segment partitioning lineage; a mismatch means the publish would change the core partition layout of existing data, which core partition sets forbid. Druid throws this ISE to protect lineage consistency.","triggerScenarios":"Publishing append/replace segments whose start/end root partition ids differ from the existing segments for the same interval — e.g. an overwrite with different partitioning than the original ingestion, or a task retry with a stale segment set.","commonSituations":"Re-running compaction with a different partitioning config against an interval already compacted differently; supervisor tasks restarted after the segment layout changed; hand-edited or stale taskspec reusing old segment ids.","solutions":["Align the new taskspec's partitioning (num SHARDS/root partitions) with the existing segments for that interval, or use a higher minor version replace that covers the full root partition range.","Reset/redo the interval: kill the old segments and re-ingest with the desired partitioning.","Check the publish/replace spec so it targets the same root partitions as the segments being replaced.","Verify you are not replaying a stale task payload referencing old partitioning."],"exampleFix":"null","handlingStrategy":"validation","validationCode":"int oldStart = oldSegments.get(0).getStartRootPartitionId();\nint oldEnd   = oldSegments.get(oldSegments.size()-1).getEndRootPartitionId();\nint newStart = newSegments.get(0).getStartRootPartitionId();\nint newEnd   = newSegments.get(newSegments.size()-1).getEndRootPartitionId();\nif (oldStart != newStart || oldEnd != newEnd) {\n  throw new IllegalStateException(\"root partition range mismatch: fix partitioning spec or kill+re-ingest\");\n}","typeGuard":null,"tryCatchPattern":"try { result = action.perform(task, toolbox); } catch (ISE e) { if (e.getMessage().contains(\"Root partition range\")) { /* align partitioning or re-ingest interval */ } else throw e; }","preventionTips":["Keep partitioning configuration stable for a given interval across retries.","Use replace/compaction with matching root partitions, or full-coverage replace with a bumped minor version.","Kill and re-ingest when changing partition layout for existing intervals.","Diff old vs new segment partition info before publishing."],"tags":["druid","segment-publish","partitioning","lineage"],"backgroundTag":"schema-validation-failed","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"}