apache/druid · error · IllegalArgumentException
Should only have one interval, got
Error message
Should only have one interval, got[%s]
What it means
GroupBy processing requires the query's querySegmentSpec to contain exactly one interval. validateForProcess throws this IAE when the interval list is empty or has multiple intervals, since the cursor-holder based processing path can only iterate a single interval.
Solutions
- Issue one query per interval, or use a single covering interval
- Use multipleIntervals handling at the tool/SQL layer (Druid SQL splits ranges) instead of the native groupBy v2 cursor path
- Validate query.getQuerySegmentSpec().getIntervals().size() == 1 before processing
- Rewrite with a union datasource of single-interval subqueries if multiple ranges are needed
Example fix
// before
query.setInterval(Arrays.asList("2022-01-01/2022-02-01", "2022-03-01/2022-04-01"));
// after
query.setInterval("2022-01-01/2022-04-01"); // single covering interval, or split into two queries Defensive patterns
Strategy: validation
Validate before calling
List<Interval> intervals = query.getQuerySegmentSpec().getIntervals();
if (intervals.size() != 1) {
throw new IAE("groupBy cursor path needs exactly one interval, got %d", intervals.size());
} Try / catch
try { engine.process(query, ...); } catch (IAE e) { if (e.getMessage().startsWith("Should only have one interval")) { splitAndRunPerInterval(query); } else { throw e; } } Prevention
- Always build groupBy queries with a single interval string
- Split multi-range requests into multiple queries at the client layer
- Validate intervals before submitting native queries
When it happens
Trigger: Issuing a GroupByQuery (via GroupingEngine.process/makeCursorHolderAsync/processCursorHolder) whose getQuerySegmentSpec().getIntervals() returns a list with size != 1 — e.g. multiple explicit intervals like ["2012-01-01/2012-01-02","2012-02-01/2012-02-02"] or none.
Common situations: Programmatically building a query with multiple intervals; client SDKs accepting interval arrays; migrating code from the legacy engine that accepted multiple intervals; converting SQL with disjoined time ranges.
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
- buffer for list is too small, was
- Distinct intervals in input segments may not overlap
- Duplicate output name
- Group key should be a single dimension
- Illegal number of fields
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/d71fc46ae4f358be.
Report an issue: GitHub.
Appendix: source
Thrown at processing/src/main/java/org/apache/druid/query/groupby/GroupingEngine.java:537
@Nullable GroupByQueryMetrics groupByQueryMetrics
)
{
validateForProcess(query, cursorFactory);
final CursorBuildSpec buildSpec = makeCursorBuildSpec(query, groupByQueryMetrics);
return processWithCursorHolder(query, cursorFactory, cursorHolder, timeBoundaryInspector, bufferPool, buildSpec);
}
private static void validateForProcess(GroupByQuery query, @Nullable CursorFactory cursorFactory)
{
if (cursorFactory == null) {
throw new ISE(
"Null cursor factory found. Probably trying to issue a query against a segment being memory unmapped."
);
}
final List<Interval> intervals = query.getQuerySegmentSpec().getIntervals();
if (intervals.size() != 1) {
throw new IAE("Should only have one interval, got[%s]", intervals);
}
}
private Sequence<ResultRow> processWithCursorHolder(
GroupByQuery query,
CursorFactory cursorFactory,
CursorHolder cursorHolder,
@Nullable TimeBoundaryInspector timeBoundaryInspector,
NonBlockingPool<ByteBuffer> bufferPool,
CursorBuildSpec buildSpec
)
{
// Register the cursor holder on the closer before any work that could throw, so a single catch path covers
// every cleanup scenario (bufferPool.take() failure, pipeline construction failure, etc.) and the original
// exception is preserved with any close errors as suppressed.
final Closer closer = Closer.create();
closer.register(cursorHolder);
View on GitHub (pinned to 9b90983fd2)