{"record":{"id":"e7e4989aa7e7fd6f","repo":"apache/druid","slug":"found-different-granularities-in-segments-s","errorCode":null,"errorMessage":"Found different granularities in segments %s","messagePattern":"Found different granularities in segments (.+?)","errorType":"exception","errorClass":"ISE","httpStatus":null,"severity":"error","filePath":"indexing-service/src/main/java/org/apache/druid/indexing/common/task/TaskLockHelper.java","lineNumber":178,"sourceCode":"              knownSegmentGranularity,\n              segments\n          );\n        }\n        final List<DataSegment> nonAlignedSegments = segments\n            .stream()\n            .filter(segment -> !knownSegmentGranularity.isAligned(segment.getInterval()))\n            .collect(Collectors.toList());\n\n        if (!nonAlignedSegments.isEmpty()) {\n          throw new ISE(\n              \"Non-aligned segments %s for granularity[%s]\",\n              SegmentUtils.commaSeparatedIdentifiers(nonAlignedSegments),\n              knownSegmentGranularity\n          );\n        }\n      }\n    } else {\n      throw new ISE(\n          \"Found different granularities in segments %s\",\n          SegmentUtils.commaSeparatedIdentifiers(segments)\n      );\n    }\n  }\n\n  private boolean tryLockSegments(TaskActionClient actionClient, List<DataSegment> segments) throws IOException\n  {\n    final Map<Interval, List<DataSegment>> intervalToSegments = SegmentUtils.groupSegmentsByInterval(segments);\n    for (Entry<Interval, List<DataSegment>> entry : intervalToSegments.entrySet()) {\n      final Interval interval = entry.getKey();\n      final List<DataSegment> segmentsInInterval = entry.getValue();\n      final boolean hasSameVersion = segmentsInInterval\n          .stream()\n          .allMatch(segment -> segment.getVersion().equals(segmentsInInterval.get(0).getVersion()));\n      Preconditions.checkState(\n          hasSameVersion,\n          \"Segments %s should have same version\",","sourceCodeStart":160,"sourceCodeEnd":196,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/indexing-service/src/main/java/org/apache/druid/indexing/common/task/TaskLockHelper.java#L160-L196","documentation":"Thrown by TaskLockHelper.verifySegmentGranularity when AbstractBatchIndexTask.findGranularityFromSegments returns null, meaning the input segments passed to a segment-lock batch/compaction task do not share a single inferable segment granularity. The helper must establish one known granularity before locking existing segments for overwrite; if segments in the batch disagree on granularity, none can be inferred and the task aborts. This guard ensures output segments can overshadow all input segments consistently.","triggerScenarios":"verifyAndLockExistingSegments -> verifySegmentGranularity with a list of existing segments whose intervals imply more than one granularity (e.g. a mix of 'day' and 'hour' aligned segments, or segments spanning inconsistent interval boundaries).","commonSituations":"Compaction or batch overwrite configured against a datasource whose segments were ingested with different segmentGranularity settings over time (e.g. historical data at 'month', newer data at 'day'); manual segment lists assembled from mixed intervals; retuning granularitySpec.segmentGranularity while old segments remain.","solutions":["Set the compaction/batch task input to segments sharing one granularity (filter by interval so all segments align to a single period).","If granularities differ intentionally, run separate compaction/overwrite tasks per granularity group.","Unify the datasource granularity by re-ingesting or compacting so all segments share the same segmentGranularity going forward.","Check the task's granularitySpec/segmentGranularity config matches the granularity actually present in the input segments."],"exampleFix":"// before: mixed segments fed to one compaction task\nList<DataSegment> segments = getSegments(ds); // contains day- and hour-granular segments\nhelper.verifyAndLockExistingSegments(client, segments);\n\n// after: group by inferred granularity and compact each group separately\nMap<Granularity, List<DataSegment>> byGran = groupByGranularity(getSegments(ds));\nbyGran.values().forEach(g -> helper.verifyAndLockExistingSegments(client, g));","handlingStrategy":"validation","validationCode":"// Java: verify all segments infer to one granularity before submitting the task\nGranularity g = AbstractBatchIndexTask.findGranularityFromSegments(inputSegments);\nif (g == null) {\n  throw new IllegalStateException(\"Input segments span multiple granularities; split the batch by granularity\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  taskClient.runCompaction(ds, interval);\n} catch (IllegalStateException e) {\n  if (e.getMessage().contains(\"Found different granularities\")) {\n    // split input segments by granularity and run one task per group\n  } else { throw e; }\n}","preventionTips":["Keep a single segmentGranularity per datasource; change it only via a full re-ingest/replace.","Scope compaction/overwrite task intervals so they cannot span segments of different granularities.","Audit the segments table for mixed-granularity intervals before configuring compaction."],"tags":["druid","segment-granularity","compaction","segment-lock"],"backgroundTag":"inconsistent-granularity-segments","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"}