apache/druid · error · ISE

Only PeriodGranulaity is supported for movingAverage queries

Error message

Only PeriodGranulaity is supported for movingAverage queries

What it means

MovingAverageQueryRunner.run only supports PeriodGranularity for the moving-average bucket computation, since it derives past buckets by applying the period to intervals. Any other granularity (e.g. duration or period-less granularities) reaches the else branch and throws IllegalStateException.

Source

Thrown at extensions-contrib/moving-average-query/src/main/java/org/apache/druid/query/movingaverage/MovingAverageQueryRunner.java:104

    MovingAverageQuery maq = (MovingAverageQuery) query.getQuery();
    List<Interval> intervals;
    final Period period;

    // Get the largest bucket from the list of averagers
    Optional<Integer> opt =
        maq.getAveragerSpecs().stream().map(AveragerFactory::getNumBuckets).max(Integer::compare);
    int buckets = opt.orElse(0);

    //Extend the interval beginning by specified bucket - 1
    if (maq.getGranularity() instanceof PeriodGranularity) {
      period = ((PeriodGranularity) maq.getGranularity()).getPeriod();
      int offset = buckets <= 0 ? 0 : (1 - buckets);
      intervals = maq.getIntervals()
                     .stream()
                     .map(i -> new Interval(i.getStart().withPeriodAdded(period, offset), i.getEnd()))
                     .collect(Collectors.toList());
    } else {
      throw new ISE("Only PeriodGranulaity is supported for movingAverage queries");
    }

    Sequence<Row> resultsSeq;
    DataSource dataSource = maq.getDataSource();
    if (maq.getDimensions() != null && !maq.getDimensions().isEmpty() &&
        (dataSource instanceof TableDataSource || dataSource instanceof UnionDataSource ||
         dataSource instanceof QueryDataSource)) {
      // build groupBy query from movingAverage query
      GroupByQuery.Builder builder = GroupByQuery.builder()
                                                 .setDataSource(dataSource)
                                                 .setInterval(intervals)
                                                 .setDimFilter(maq.getFilter())
                                                 .setGranularity(maq.getGranularity())
                                                 .setDimensions(maq.getDimensions())
                                                 .setAggregatorSpecs(maq.getAggregatorSpecs())
                                                 .setPostAggregatorSpecs(maq.getPostAggregatorSpecs())
                                                 .setContext(maq.getContext());
      GroupByQuery gbq = builder.build();

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Change the query granularity to a period-based one, e.g. {"type": "period", "period": "P1D"}
  2. Use ISO-8601 periods (PT1H, P7D) that match the desired averaging window
  3. If building the query programmatically, construct new PeriodGranularity(new Period(...), null, tz) instead of DurationGranularity

Example fix

// before
"granularity": {"type": "duration", "duration": 86400000}
// after
"granularity": {"type": "period", "period": "P1D", "timeZone": "UTC"}
Defensive patterns

Strategy: validation

Validate before calling

Granularity g = maq.getGranularity();
if (!(g instanceof PeriodGranularity)) {
  throw new IllegalArgumentException("movingAverage requires period granularity, got: " + g.getClass().getSimpleName());
}

Type guard

boolean isPeriod(Granularity g) { return g instanceof PeriodGranularity; }

Try / catch

try { results = runner.run(sequence, responseContext); } catch (IllegalStateException e) { if (e.getMessage().contains("PeriodGranulaity")) { /* rebuild query with period granularity */ } }

Prevention

When it happens

Trigger: Running a movingAverage/averager query whose 'granularity' is not a PeriodGranularity, e.g. {"type": "duration", "duration": 3600000} or a simple "day" string granularity.

Common situations: Users familiar with standard group-by queries reusing duration granularities in moving-average queries; SQL-generated or templated queries that always emit duration granularity; misconfigured dashboards.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/a912be29fe0b7ebe. Report an issue: GitHub.