apache/druid · error · ISE

Found a different granularity from knownSegmentGranularity

Error message

Found a different granularity from knownSegmentGranularity[%s] in segments[%s]

What it means

TaskLockHelper.verifySegmentGranularity computes the single granularity shared by the existing segments to be locked/replaced and enforces that all segments share one granularity. When a second batch of segments exhibits a granularity different from the already-recorded knownSegmentGranularity, Druid throws ISE because mixed-granularity replacement would make lineage/lock bookkeeping ambiguous.

Solutions

  1. Align the datasource by compacting the target interval so all segments share one granularity, then re-run the replace/compact task.
  2. Set the task's segmentGranularity to match the granularity actually present in the segments being replaced.
  3. Split the task into per-granularity intervals so each invocation only overlaps homogeneous-granularity segments.

Example fix

// before
// task spec with segmentGranularity=P1D over a range containing hourly+daily segments
"segmentGranularity": "day"
// after: first run a compact task to unify granularity, or match existing data
"segmentGranularity": "hour" // matches knownSegmentGranularity found in segments
Defensive patterns

Strategy: validation

Validate before calling

Granularity found = AbstractBatchIndexTask.findGranularityFromSegments(segments);
if (found != null && !found.equals(expectedSegmentGranularity)) {
  throw new IllegalArgumentException("Segment granularity mismatch: segments=" + found + " task=" + expectedSegmentGranularity);
}

Type guard

boolean hasUniformGranularity(List<DataSegment> segments) {
  return AbstractBatchIndexTask.findGranularityFromSegments(segments) != null;
}

Try / catch

try {
  lockHelper.verifyAndLockExistingSegments(task, interval, segments);
} catch (IllegalStateException e) {
  if (e.getMessage().contains("Found a different granularity")) {
    log.error("Compact the interval to a single granularity before replacing", e);
  } else { throw e; }
}

Prevention

When it happens

Trigger: verifyAndLockExistingSegments is called with segment sets of differing granularities for the same datasource — e.g. replace/compact tasks over intervals that contain both hourly and daily segments, or a granularitySpec segmentGranularity that changed between ingestion runs so the overlapped range holds mixed-granularity segments.

Common situations: Compaction over an interval spanning data ingested at different segmentGranularity settings; switching segmentGranularity in a spec and then replacing the old mixed range; reindexing a datasource that historically mixed granularities.

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

Appendix: source

Thrown at indexing-service/src/main/java/org/apache/druid/indexing/common/task/TaskLockHelper.java:158

      return true;
    }

    verifySegmentGranularity(segmentsToLock);
    return tryLockSegments(actionClient, segmentsToLock);
  }

  /**
   * Check if segmentGranularity has changed.
   */
  private void verifySegmentGranularity(List<DataSegment> segments)
  {
    final Granularity granularityFromSegments = AbstractBatchIndexTask.findGranularityFromSegments(segments);
    if (granularityFromSegments != null) {
      if (knownSegmentGranularity == null) {
        knownSegmentGranularity = granularityFromSegments;
      } else {
        if (!knownSegmentGranularity.equals(granularityFromSegments)) {
          throw new ISE(
              "Found a different granularity from knownSegmentGranularity[%s] in segments[%s]",
              knownSegmentGranularity,
              segments
          );
        }
        final List<DataSegment> nonAlignedSegments = segments
            .stream()
            .filter(segment -> !knownSegmentGranularity.isAligned(segment.getInterval()))
            .collect(Collectors.toList());

        if (!nonAlignedSegments.isEmpty()) {
          throw new ISE(
              "Non-aligned segments %s for granularity[%s]",
              SegmentUtils.commaSeparatedIdentifiers(nonAlignedSegments),
              knownSegmentGranularity
          );
        }
      }

View on GitHub (pinned to 9b90983fd2)