apache/druid · error · IllegalStateException
Cannot handle subquery: %s
Error message
Cannot handle subquery: %s
What it means
Thrown by SinkQuerySegmentWalker.getQueryRunnerForSegments when a query's dataSource is a QueryDataSource (subquery) whose inner query's tool chest cannot perform subqueries. Druid throws this to reject running a subquery against this segment walker's query type because the inner query runner factory does not support it.
Source
Thrown at server/src/main/java/org/apache/druid/segment/realtime/appenderator/SinkQuerySegmentWalker.java:196
// Sanity check: make sure the query is based on the table we're meant to handle.
if (!ev.getBaseTableDataSource().getName().equals(dataSource)) {
throw new ISE("Cannot handle datasource: %s", dataSourceFromQuery);
}
final QueryRunnerFactory<T, Query<T>> factory = conglomerate.findFactory(query);
if (factory == null) {
throw new ISE("Unknown query type[%s].", query.getClass());
}
final QueryToolChest<T, Query<T>> toolChest = factory.getToolchest();
final boolean skipIncrementalSegment = query.context().getBoolean(CONTEXT_SKIP_INCREMENTAL_SEGMENT, false);
final AtomicLong cpuTimeAccumulator = new AtomicLong(0L);
// Make sure this query type can handle the subquery, if present.
if ((dataSourceFromQuery instanceof QueryDataSource)
&& !toolChest.canPerformSubquery(((QueryDataSource) dataSourceFromQuery).getQuery())) {
throw new ISE("Cannot handle subquery: %s", dataSourceFromQuery);
}
// segmentMapFn maps each base Segment into a joined Segment if necessary.
final SegmentMapFunction segmentMapFn = JvmUtils.safeAccumulateThreadCpuTime(
cpuTimeAccumulator,
() -> ev.createSegmentMapFunction(policyEnforcer)
);
// We compute the join cache key here itself so it doesn't need to be re-computed for every segment
final Optional<byte[]> cacheKeyPrefix = Optional.ofNullable(query.getDataSource().getCacheKey());
// We need to report data for each Sink all-or-nothing, which means we need to acquire references for all
// subsegments (FireHydrants) of a segment (Sink) at once. To ensure they are properly released even when a
// query fails or is canceled, we acquire *all* sink reference upfront, and release them all when the main
// QueryRunner returned by this method is closed. (We can't do the acquisition and releasing at the level of
// each FireHydrant's runner, since then it wouldn't be properly all-or-nothing on a per-Sink basis.)
final List<SinkSegmentReference> allSegmentReferences = new ArrayList<>();
// Distinct Sinks (Druid segments) actually queried by this node. Tracked separately fromView on GitHub (pinned to 9b90983fd2)
Solutions
- Rewrite the query to avoid nesting the unsupported inner query type (e.g. replace the inner query with a direct datasource or a supported query type).
- Use a query type for the inner query that supports subqueries (groupBy/topN in Classic engine) or run the subquery separately and pass its results as an inline datasource.
- If using SQL, translate the SQL differently (avoid unsupported nesting) or ensure the query runs on the broker/MSQ engine which supports subqueries.
Example fix
// before
new TopNQueryBuilder...query(new TableDataSource("x"))... but wrapping a scan as subquery
GroupByQuery sub = new GroupByQuery.Builder()...build();
QueryDataSource ds = new QueryDataSource(new ScanQuery...); // scan subquery not supported here
// after
// use a supported inner query type, or materialize results into an inline datasource
InlineDataSource ds = InlineDataSource.fromIterable(results, rowSignature); Defensive patterns
Strategy: validation
Validate before calling
// Java: check subquery support before issuing
if (dataSource instanceof QueryDataSource) {
QueryToolChest<?, ?> chest = factory.getToolchest();
if (!chest.canPerformSubquery(((QueryDataSource) dataSource).getQuery())) {
throw new IllegalArgumentException("Subquery not supported for this query type");
}
} Try / catch
try { runner.query(query) } catch (IllegalStateException e) { if (e.getMessage().startsWith("Cannot handle subquery")) { /* rewrite query without subquery */ } else throw e; } Prevention
- Prefer groupBy/topN inner queries when using native subqueries
- Materialize subquery results to inline datasource when unsure
- Test nested SQL plans against the target engine before production
When it happens
Trigger: Issuing a query whose datasource is a subquery (QueryDataSource) where the inner query's QueryToolChest.canPerformSubquery returns false; e.g. routing a topN/groupBy-with-subquery against a segment walker backed by a query type lacking subquery support (such as certain datasource query types via AppenderatorAdvisor/segments path).
Common situations: Running nested queries (e.g. a scan or select inside a groupBy) in streaming/realtime paths where the underlying query runner factory doesn't support subqueries; issuing SQL that plans to an unsupported native subquery against a realtime task.
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
- Sketches have different number of values: %d and %d
- Got a null result! Segments are missing!
- Got a null list of results
- Emit called unexpectedly before service start
- unknown event type [%s]
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/f1ba27fa7d6b9ba1.
Report an issue: GitHub.