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
- Align the datasource by compacting the target interval so all segments share one granularity, then re-run the replace/compact task.
- Set the task's segmentGranularity to match the granularity actually present in the segments being replaced.
- 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
- Keep segmentGranularity stable across ingestion runs for a datasource
- Compact mixed-granularity ranges before running replace/append tasks
- Scope tasks to intervals containing only one granularity of segments
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
- Non-aligned segments
- Can't find overwritingSegmentMeta for interval
- Failed to get locks for intervals
- Found different granularities in segments
- Lock interval[ ] != task interval[ ]
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)