{"record":{"id":"35886f97f2d1514d","repo":"apache/druid","slug":"unspecified-interval-s-in-granularityspec-s","errorCode":null,"errorMessage":"Unspecified interval[%s] in granularitySpec[%s]","messagePattern":"Unspecified interval\\[(.+?)\\] in granularitySpec\\[(.+?)\\]","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"indexing-service/src/main/java/org/apache/druid/indexing/common/task/AbstractBatchIndexTask.java","lineNumber":831,"sourceCode":"      throw new ISE(\"Lock revoked: [%s]\", revokedLock);\n    }\n    final Map<Interval, String> versions = locks.stream().collect(\n        Collectors.toMap(TaskLock::getInterval, TaskLock::getVersion)\n    );\n\n    final Interval interval;\n    final String version;\n    if (!materializedBucketIntervals.isEmpty()) {\n      // If granularity spec has explicit intervals, we just need to find the version associated to the interval.\n      // This is because we should have gotten all required locks up front when the task starts up.\n      final Optional<Interval> maybeInterval = granularitySpec.bucketInterval(timestamp);\n      if (!maybeInterval.isPresent()) {\n        throw new IAE(\"Could not find interval for timestamp [%s]\", timestamp);\n      }\n\n      interval = maybeInterval.get();\n      if (!materializedBucketIntervals.contains(interval)) {\n        throw new ISE(\"Unspecified interval[%s] in granularitySpec[%s]\", interval, granularitySpec);\n      }\n\n      version = AbstractBatchIndexTask.findVersion(versions, interval);\n      if (version == null) {\n        throw new ISE(\"Cannot find a version for interval[%s]\", interval);\n      }\n    } else {\n      // We don't have explicit intervals. We can use the segment granularity to figure out what\n      // interval we need, but we might not have already locked it.\n      interval = granularitySpec.getSegmentGranularity().bucket(timestamp);\n      final String existingLockVersion = AbstractBatchIndexTask.findVersion(versions, interval);\n      if (existingLockVersion == null) {\n        if (ingestionSpec.getTuningConfig() instanceof ParallelIndexTuningConfig) {\n          final int maxAllowedLockCount = ((ParallelIndexTuningConfig) ingestionSpec.getTuningConfig())\n              .getMaxAllowedLockCount();\n          if (maxAllowedLockCount >= 0 && locks.size() >= maxAllowedLockCount) {\n            throw new MaxAllowedLocksExceededException(maxAllowedLockCount);\n          }","sourceCodeStart":813,"sourceCodeEnd":849,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/indexing-service/src/main/java/org/apache/druid/indexing/common/task/AbstractBatchIndexTask.java#L813-L849","documentation":"After resolving the bucket interval for a timestamp, findIntervalAndVersion checks that the interval is actually part of the granularitySpec's materialized bucket intervals. If the bucketed interval is somehow not among the explicitly specified intervals, the internal invariant between bucketing and the spec is broken, so it throws this ISE. This normally indicates a spec inconsistency rather than a data problem.","triggerScenarios":"Explicit-interval granularitySpec where bucketInterval(timestamp) returns an interval that is not contained in the materialized bucket intervals — a spec/state mismatch, e.g. intervals list misaligned with segmentGranularity boundaries after spec edits or upgrades.","commonSituations":"Hand-edited ingestion specs with intervals not aligned to segment granularity; Druid version upgrades changing bucket interval materialization; custom granularity specs built programmatically with inconsistent intervals.","solutions":["Regenerate or correct granularitySpec so that explicit intervals are exact multiples/alignments of segmentGranularity buckets.","Rewrite the spec from scratch (or via the UI/wizard) instead of hand-editing intervals.","Check the Druid version for known bugs in bucket interval materialization; upgrade if affected.","If you don't need explicit intervals, omit them so Druid derives intervals from data timestamps."],"exampleFix":"// before\n\"segmentGranularity\":\"day\", \"intervals\":[\"2024-01-01T12:00/2024-01-02\"]  // misaligned\n// after\n\"segmentGranularity\":\"day\", \"intervals\":[\"2024-01-01/2024-01-02\"]","handlingStrategy":"validation","validationCode":"// verify every bucket of the explicit intervals aligns with segmentGranularity\nfor (Interval i : granularitySpec.getQuery().getIntervals()) {\n  Interval bucket = granularitySpec.getSegmentGranularity().bucket(i.getStart());\n  if (!i.equals(bucket.getStart(), i.getEnd())) {\n    throw new IllegalArgumentException(\"Interval \" + i + \" not aligned with segmentGranularity\");\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  ingest(spec);\n} catch (IllegalStateException e) {\n  if (e.getMessage().startsWith(\"Unspecified interval\")) {\n    // regenerate the granularitySpec with correctly aligned intervals\n  } else throw e;\n}","preventionTips":["Generate specs via tooling/UI rather than hand-editing intervals.","Keep explicit intervals exact multiples of segmentGranularity.","Re-validate specs after Druid upgrades that may change interval materialization."],"tags":["indexing-service","granularity-spec","invariant"],"backgroundTag":"internal-invariant-violation","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}