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

  1. Re-ingest or compact the offending segments so their intervals align to the intended granularity.
  2. Choose a segmentGranularity in the task that matches the actual alignment of the existing segments.
  3. 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

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


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)