apache/druid · error · SegmentGranularityTimelineValidationException

Invalid segment granularity timeline for dataSource[%s]: Int

Error message

Invalid segment granularity timeline for dataSource[%s]: Interval[%s] with granularity[%s] is more recent than interval[%s] with granularity[%s], but has a coarser granularity. Segment granularity must stay the same or become coarser as data ages from present to past.

What it means

CascadingReindexingTemplate.validateSegmentGranularityTimeline throws SegmentGranularityTimelineValidationException (message about an invalid segment granularity timeline) when, walking the datasource's segment timeline from older to newer intervals, granularity becomes coarser toward the present. Cascading reindexing requires segment granularity to stay the same or get finer as data gets newer, so the cascading reindex chain can be built predictably. A datasource whose historical granularity pattern violates this (e.g., older day-granularity data followed by newer month-granularity data) cannot be cascaded.

Source

Thrown at indexing-service/src/main/java/org/apache/druid/indexing/compact/CascadingReindexingTemplate.java:822

  private void validateSegmentGranularityTimeline(List<IntervalPartitioningInfo> timeline)
  {
    if (timeline.size() <= 1) {
      return; // Nothing to validate
    }

    for (int i = 1; i < timeline.size(); i++) {
      IntervalPartitioningInfo olderInterval = timeline.get(i - 1);
      IntervalPartitioningInfo newerInterval = timeline.get(i);

      Granularity olderGran = olderInterval.getGranularity();
      Granularity newerGran = newerInterval.getGranularity();

      // As we move from past (older intervals) to present (newer intervals),
      // granularity should stay the same or get finer.
      // If the older interval's granularity is finer than the newer interval's granularity,
      // that means we're getting coarser as we move toward present, which is invalid.
      if (olderGran.isFinerThan(newerGran)) {
        throw new SegmentGranularityTimelineValidationException(
            dataSource,
            olderInterval.getInterval(),
            olderGran,
            newerInterval.getInterval(),
            newerGran
        );
      }
    }

    LOG.debug(
        "Segment granularity timeline validation passed for dataSource[%s] with [%d] intervals",
        dataSource,
        timeline.size()
    );
  }

  /**
   * Collects thresholds from all non-partitioning rules.

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Re-ingest or compact the offending intervals so the granularity timeline is monotonically finer toward the present (or uniform), then rerun cascading reindexing
  2. Exclude or separately handle the legacy intervals whose granularity breaks the rule (adjust the task's interval scope)
  3. Normalize the datasource to a single granularity via a one-time reindex of the full history
  4. Adjust ingestion specs going forward to keep granularity consistent with existing history

Example fix

// before: mixed granularity history breaks validation
// 2024: hour segments, 2025: month segments
// after: one-time reindex of the full timeline to day granularity
{
  "type": "index_parallel",
  "spec": { "dataSchema": { "granularitySpec": { "segmentGranularity": "day", "intervals": ["2024/2026"] } } }
}
Defensive patterns

Strategy: validation

Validate before calling

// check granularity monotonicity before submitting a cascading reindex task
List<TimelineObjectHolder> holders = timeline.buildExistingTimeline(dataSource, intervals);
for (int i = 1; i < holders.size(); i++) {
  Granularity older = granularityOf(holders.get(i - 1));
  Granularity newer = granularityOf(holders.get(i));
  if (older.isFinerThan(newer)) {
    throw new IAE("cascading reindex requires granularity same-or-finer toward present; %s -> %s", older, newer);
  }
}

Try / catch

catch (SegmentGranularityTimelineValidationException e) {
  log.warn("timeline violates granularity rule between %s and %s; reindexing full history to one granularity",
           e.getOlderInterval(), e.getNewerInterval());
  return submitFullReindex(dataSource, uniformGranularity("day"));
}

Prevention

When it happens

Trigger: generateBasePartitioningAlignedTimeline inspects existing segments and finds an older interval whose granularity is finer than a newer interval's granularity (olderGran.isFinerThan(newerGran) == true), e.g., hour-granularity segments in 2024 and month-granularity segments in 2025, then a cascading reindexing task is submitted for that datasource.

Common situations: Datasources whose ingestion granularity changed over time (hourly earlier, daily/monthly later); merged compaction that re-granulated only recent data; manual ingestion with mixed segmentGranularity settings; re-enabling cascading reindexing after such history changes.

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