apache/druid · error · ISE
Found different granularities in segments %s
Error message
Found different granularities in segments %s
What it means
Thrown by TaskLockHelper.verifySegmentGranularity when AbstractBatchIndexTask.findGranularityFromSegments returns null, meaning the input segments passed to a segment-lock batch/compaction task do not share a single inferable segment granularity. The helper must establish one known granularity before locking existing segments for overwrite; if segments in the batch disagree on granularity, none can be inferred and the task aborts. This guard ensures output segments can overshadow all input segments consistently.
Source
Thrown at indexing-service/src/main/java/org/apache/druid/indexing/common/task/TaskLockHelper.java:178
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
);
}
}
} else {
throw new ISE(
"Found different granularities in segments %s",
SegmentUtils.commaSeparatedIdentifiers(segments)
);
}
}
private boolean tryLockSegments(TaskActionClient actionClient, List<DataSegment> segments) throws IOException
{
final Map<Interval, List<DataSegment>> intervalToSegments = SegmentUtils.groupSegmentsByInterval(segments);
for (Entry<Interval, List<DataSegment>> entry : intervalToSegments.entrySet()) {
final Interval interval = entry.getKey();
final List<DataSegment> segmentsInInterval = entry.getValue();
final boolean hasSameVersion = segmentsInInterval
.stream()
.allMatch(segment -> segment.getVersion().equals(segmentsInInterval.get(0).getVersion()));
Preconditions.checkState(
hasSameVersion,
"Segments %s should have same version",View on GitHub (pinned to 9b90983fd2)
Solutions
- Set the compaction/batch task input to segments sharing one granularity (filter by interval so all segments align to a single period).
- If granularities differ intentionally, run separate compaction/overwrite tasks per granularity group.
- Unify the datasource granularity by re-ingesting or compacting so all segments share the same segmentGranularity going forward.
- Check the task's granularitySpec/segmentGranularity config matches the granularity actually present in the input segments.
Example fix
// before: mixed segments fed to one compaction task List<DataSegment> segments = getSegments(ds); // contains day- and hour-granular segments helper.verifyAndLockExistingSegments(client, segments); // after: group by inferred granularity and compact each group separately Map<Granularity, List<DataSegment>> byGran = groupByGranularity(getSegments(ds)); byGran.values().forEach(g -> helper.verifyAndLockExistingSegments(client, g));
Defensive patterns
Strategy: validation
Validate before calling
// Java: verify all segments infer to one granularity before submitting the task
Granularity g = AbstractBatchIndexTask.findGranularityFromSegments(inputSegments);
if (g == null) {
throw new IllegalStateException("Input segments span multiple granularities; split the batch by granularity");
} Try / catch
try {
taskClient.runCompaction(ds, interval);
} catch (IllegalStateException e) {
if (e.getMessage().contains("Found different granularities")) {
// split input segments by granularity and run one task per group
} else { throw e; }
} Prevention
- Keep a single segmentGranularity per datasource; change it only via a full re-ingest/replace.
- Scope compaction/overwrite task intervals so they cannot span segments of different granularities.
- Audit the segments table for mixed-granularity intervals before configuring compaction.
When it happens
Trigger: verifyAndLockExistingSegments -> verifySegmentGranularity with a list of existing segments whose intervals imply more than one granularity (e.g. a mix of 'day' and 'hour' aligned segments, or segments spanning inconsistent interval boundaries).
Common situations: Compaction or batch overwrite configured against a datasource whose segments were ingested with different segmentGranularity settings over time (e.g. historical data at 'month', newer data at 'day'); manual segment lists assembled from mixed intervals; retuning granularitySpec.segmentGranularity while old segments remain.
Related errors
- Can't compact segments of non-consecutive rootPartition rang
- All atomicUpdateGroup must be compacted together. Expected s
- Interval[%s] is empty, must specify a nonempty interval
- Conflicting segment granularities found %s(segmentGranularit
- Unknown tuningConfig type: [%s], Must be in [%s, %s, %s]
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/e7e4989aa7e7fd6f.
Report an issue: GitHub.