apache/druid · error · ISE
Non-aligned segments
Error message
Non-aligned segments %s for granularity[%s]
What it means
After confirming a single knownSegmentGranularity, verifySegmentGranularity checks that every segment's interval is actually aligned to that granularity (interval start/end on bucket boundaries). Segments whose intervals don't align to the granularity (e.g. custom, non-uniform intervals) cannot be safely locked and replaced, so Druid throws ISE listing the offending segment identifiers.
Solutions
- Re-ingest or compact the offending segments so their intervals align to the intended granularity.
- Choose a segmentGranularity in the task that matches the actual alignment of the existing segments.
- Delete/kill the misaligned unused segments (they may be historical leftovers) and re-run the task.
Example fix
// before
// task granularity "hour" against segments with 37-minute custom intervals -> ISE
"segmentGranularity": "hour"
// after
"segmentGranularity": {"type": "period", "period": "PT37M"} // or realign segments via compaction Defensive patterns
Strategy: validation
Validate before calling
List<DataSegment> misaligned = segments.stream()
.filter(s -> !granularity.isAligned(s.getInterval()))
.collect(Collectors.toList());
if (!misaligned.isEmpty()) {
throw new IllegalArgumentException("Unaligned segments: " + misaligned);
} Type guard
boolean isAligned(DataSegment s, Granularity g) { return g.isAligned(s.getInterval()); } Try / catch
try {
lockHelper.verifyAndLockExistingSegments(task, interval, segments);
} catch (IllegalStateException e) {
if (e.getMessage().startsWith("Non-aligned segments")) {
log.error("Realign or kill misaligned segments before replacing", e);
} else { throw e; }
} Prevention
- Avoid custom/non-uniform segment intervals unless necessary
- Verify segment interval alignment with Granularity.isAligned before replace tasks
- Compact historical misaligned segments to standard granularities
- Audit segment metadata after custom shardSpec or interval overrides
When it happens
Trigger: verifyAndLockExistingSegments encounters segments whose getInterval() is not bucket-aligned to knownSegmentGranularity — e.g. segments created with irregular/unaligned intervals (custom shardSpecs, partial-range compaction artifacts, or hand-crafted segments) while the task uses a regular granularity like hour/day.
Common situations: Datasources polluted by segments produced with non-aligned intervals from custom granularity specs or older ingestion runs; compaction with mismatched segmentGranularity; manually adjusted segment metadata.
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
- Found a different granularity from knownSegmentGranularity
- 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/1cf81a7f2c0e2cc2.
Report an issue: GitHub.
Appendix: source
Thrown at indexing-service/src/main/java/org/apache/druid/indexing/common/task/TaskLockHelper.java:170
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
);
}
}
} 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()) {View on GitHub (pinned to 9b90983fd2)