apache/druid · error · IllegalStateException

New segment[ ] have a smaller minor version than old…

Error message

New segment[%s] have a smaller minor version than old segment[%s]

What it means

In SegmentTransactionalInsertAction.checkWithSegmentLock(), every new segment for an interval must have a strictly greater minor version than each existing (old) segment it replaces. Minor versions order concurrent revisions within the same major version; publishing a segment with an equal or smaller minor version would shadow or tie with existing data, so Druid throws this ISE and aborts the transactional insert.

Solutions

  1. Restart the task so it re-allocates fresh segment identifiers with a higher minor version via SEGMENT_ALLOCATE.
  2. Ensure only one task overwrites a given interval at a time; serialize overwrite/compaction jobs.
  3. Kill conflicting already-published segments if they should be replaced, then re-run the task.
  4. Check task retry logic so it does not reuse cached SegmentIdWithShardSpec values across attempts.

Example fix

null
Defensive patterns

Strategy: retry

Validate before calling

for (SegmentIdWithShardSpec n : newSegments)
  for (SegmentIdWithShardSpec o : oldSegments)
    if (n.getMinorVersion() <= o.getMinorVersion())
      throw new IllegalStateException("new segment " + n + " must have greater minor version than " + o);

Try / catch

try { result = action.perform(task, toolbox); } catch (ISE e) { if (e.getMessage().contains("smaller minor version")) { /* restart task to allocate fresh, higher-version segment ids */ } else throw e; }

Prevention

When it happens

Trigger: Retrying an old task whose segment ids carry a minor version already used by segments published by a competing task; two tasks generating segments with the same minor version for the same interval/chunk; replaying a stale publish after another task already upgraded the interval.

Common situations: Concurrent overwrite tasks writing to the same time chunk; task retries after partial publish where a newer segment already exists; supervisor tasks racing during failover.

Related errors


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

Appendix: source

Thrown at indexing-service/src/main/java/org/apache/druid/indexing/common/actions/SegmentTransactionalInsertAction.java:306

      final int newStartRootPartitionId = newSegmentsPerInterval.get(0).getStartRootPartitionId();
      final int newEndRootPartitionId = newSegmentsPerInterval.get(newSegmentsPerInterval.size() - 1)
                                                              .getEndRootPartitionId();

      if (oldStartRootPartitionId != newStartRootPartitionId || oldEndRootPartitionId != newEndRootPartitionId) {
        throw new ISE(
            "Root partition range[%d, %d] of new segments doesn't match to root partition range[%d, %d] of old segments",
            newStartRootPartitionId,
            newEndRootPartitionId,
            oldStartRootPartitionId,
            oldEndRootPartitionId
        );
      }

      newSegmentsPerInterval
          .forEach(eachNewSegment -> oldSegmentsPerInterval
              .forEach(eachOldSegment -> {
                if (eachNewSegment.getMinorVersion() <= eachOldSegment.getMinorVersion()) {
                  throw new ISE(
                      "New segment[%s] have a smaller minor version than old segment[%s]",
                      eachNewSegment,
                      eachOldSegment
                  );
                }
              }));
    });
  }

  private static Map<Interval, List<DataSegment>> groupSegmentsByIntervalAndSort(Set<DataSegment> segments)
  {
    final Map<Interval, List<DataSegment>> segmentsMap = new HashMap<>();
    segments.forEach(segment -> segmentsMap.computeIfAbsent(segment.getInterval(), k -> new ArrayList<>())
                                           .add(segment));
    segmentsMap.values().forEach(segmentsPerInterval -> segmentsPerInterval.sort((s1, s2) -> {
      if (s1.getStartRootPartitionId() != s2.getStartRootPartitionId()) {
        return Integer.compare(s1.getStartRootPartitionId(), s2.getStartRootPartitionId());
      } else {

View on GitHub (pinned to 9b90983fd2)