{"record":{"id":"15d00ac96c6fed14","repo":"apache/druid","slug":"invalid-segment-granularity-timeline-for-datasourc","errorCode":null,"errorMessage":"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.","messagePattern":"Invalid segment granularity timeline for dataSource\\[(.+?)\\]: Interval\\[(.+?)\\] with granularity\\[(.+?)\\] is more recent than interval\\[(.+?)\\] with granularity\\[(.+?)\\], but has a coarser granularity\\. Segment granularity must stay the same or become coarser as data ages from present to past\\.","errorType":"validation","errorClass":"SegmentGranularityTimelineValidationException","httpStatus":null,"severity":"error","filePath":"indexing-service/src/main/java/org/apache/druid/indexing/compact/CascadingReindexingTemplate.java","lineNumber":822,"sourceCode":"  private void validateSegmentGranularityTimeline(List<IntervalPartitioningInfo> timeline)\n  {\n    if (timeline.size() <= 1) {\n      return; // Nothing to validate\n    }\n\n    for (int i = 1; i < timeline.size(); i++) {\n      IntervalPartitioningInfo olderInterval = timeline.get(i - 1);\n      IntervalPartitioningInfo newerInterval = timeline.get(i);\n\n      Granularity olderGran = olderInterval.getGranularity();\n      Granularity newerGran = newerInterval.getGranularity();\n\n      // As we move from past (older intervals) to present (newer intervals),\n      // granularity should stay the same or get finer.\n      // If the older interval's granularity is finer than the newer interval's granularity,\n      // that means we're getting coarser as we move toward present, which is invalid.\n      if (olderGran.isFinerThan(newerGran)) {\n        throw new SegmentGranularityTimelineValidationException(\n            dataSource,\n            olderInterval.getInterval(),\n            olderGran,\n            newerInterval.getInterval(),\n            newerGran\n        );\n      }\n    }\n\n    LOG.debug(\n        \"Segment granularity timeline validation passed for dataSource[%s] with [%d] intervals\",\n        dataSource,\n        timeline.size()\n    );\n  }\n\n  /**\n   * Collects thresholds from all non-partitioning rules.","sourceCodeStart":804,"sourceCodeEnd":840,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/indexing-service/src/main/java/org/apache/druid/indexing/compact/CascadingReindexingTemplate.java#L804-L840","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Re-ingest or compact the offending intervals so the granularity timeline is monotonically finer toward the present (or uniform), then rerun cascading reindexing","Exclude or separately handle the legacy intervals whose granularity breaks the rule (adjust the task's interval scope)","Normalize the datasource to a single granularity via a one-time reindex of the full history","Adjust ingestion specs going forward to keep granularity consistent with existing history"],"exampleFix":"// before: mixed granularity history breaks validation\n// 2024: hour segments, 2025: month segments\n// after: one-time reindex of the full timeline to day granularity\n{\n  \"type\": \"index_parallel\",\n  \"spec\": { \"dataSchema\": { \"granularitySpec\": { \"segmentGranularity\": \"day\", \"intervals\": [\"2024/2026\"] } } }\n}\n","handlingStrategy":"validation","validationCode":"// check granularity monotonicity before submitting a cascading reindex task\nList<TimelineObjectHolder> holders = timeline.buildExistingTimeline(dataSource, intervals);\nfor (int i = 1; i < holders.size(); i++) {\n  Granularity older = granularityOf(holders.get(i - 1));\n  Granularity newer = granularityOf(holders.get(i));\n  if (older.isFinerThan(newer)) {\n    throw new IAE(\"cascading reindex requires granularity same-or-finer toward present; %s -> %s\", older, newer);\n  }\n}","typeGuard":null,"tryCatchPattern":"catch (SegmentGranularityTimelineValidationException e) {\n  log.warn(\"timeline violates granularity rule between %s and %s; reindexing full history to one granularity\",\n           e.getOlderInterval(), e.getNewerInterval());\n  return submitFullReindex(dataSource, uniformGranularity(\"day\"));\n}","preventionTips":["Keep segmentGranularity consistent across ingestion specs for a datasource over time","One-time reindex legacy intervals if granularity was changed historically","Review compaction templates for granularity changes applied only to recent data","Preview the segment timeline (sys.segments table) before enabling cascading reindexing"],"tags":["compaction","reindexing","segment-granularity","validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}