apache/druid · error · QueryUnsupportedException
Cannot handle subquery
Error message
Cannot handle subquery: %s
What it means
ServerManager.buildQueryToolChest (getQueryToolChest) verifies that the query's QueryToolChest supports subqueries via canPerformSubquery. If the top-level query's dataSource is a QueryDataSource (subquery) and the toolchest cannot perform subqueries, it throws QueryUnsupportedException naming the dataSource.
Solutions
- Rewrite the query so the outer query does not take a QueryDataSource (e.g. run subquery separately, or use SQL which plans supported subqueries)
- Use a query type that supports subqueries (groupBy, timeseries, scan) as the outer query
- Upgrade Druid if the subquery support was added for that query type in later releases
- If using a custom query type, implement canPerformSubquery in its QueryToolChest
Example fix
// before GroupByQuery.builder().setDataSource(new QueryDataSource(topNQuery))... // after // run the subquery first and use its results via SQL or a table/inline datasource client.run(subQuery); // then build outer query against materialized results
Defensive patterns
Strategy: try-catch
Validate before calling
DataSource ds = query.getDataSource();
if (ds instanceof QueryDataSource
&& !factory.getToolchest().canPerformSubquery(((QueryDataSource) ds).getQuery())) {
throw new UnsupportedOperationException("Query type cannot handle subquery: " + ds);
} Type guard
boolean supportsSubquery(QueryToolChest<?, ?> chest, Query<?> q) {
return !(q.getDataSource() instanceof QueryDataSource)
|| chest.canPerformSubquery(((QueryDataSource) q.getDataSource()).getQuery());
} Try / catch
try {
return serverManager.buildQueryRunner(...);
} catch (QueryUnsupportedException e) {
if (e.getMessage().startsWith("Cannot handle subquery")) {
log.warn("Query type does not support subqueries: %s", e.getMessage());
}
throw e;
} Prevention
- Check toolChest.canPerformSubquery before building nested native queries
- Prefer SQL (which plans supported subqueries) over hand-built nested native queries
- Use groupBy/timeseries/scan as outer query types when nesting
- Keep extension query types out of subquery position unless they implement canPerformSubquery
When it happens
Trigger: Running a native query whose dataSource is another query on a query type that lacks subquery support (e.g. some extension query types like topN/scan variants historically, or custom query types) against the broker.
Common situations: Wrapping an unsupported query type inside a native groupBy/scan subquery; custom extension queries used as subqueries; older versions where certain query types did not implement canPerformSubquery.
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
- Cannot query dataSource locally
- Cannot handle dataSource
- Cannot handle datasource
- Cannot handle subquery
- Cannot operate on a dimension with no dictionary
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/3f3abc8d1f0843ea.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/main/java/org/apache/druid/server/ServerManager.java:584
final QueryUnsupportedException e = new QueryUnsupportedException(
StringUtils.format("Unknown query type, [%s]", query.getClass())
);
log.makeAlert(e, "Error while executing a query[%s]", query.getId())
.addData("dataSource", dataSourceFromQuery)
.emit();
throw e;
}
return factory;
}
protected static <T> QueryToolChest<T, Query<T>> getQueryToolChest(Query<T> query, QueryRunnerFactory<T, Query<T>> factory)
{
final DataSource dataSourceFromQuery = query.getDataSource();
final QueryToolChest<T, Query<T>> toolChest = factory.getToolchest();
// Make sure this query type can handle the subquery, if present.
if ((dataSourceFromQuery instanceof QueryDataSource)
&& !toolChest.canPerformSubquery(((QueryDataSource) dataSourceFromQuery).getQuery())) {
throw new QueryUnsupportedException(StringUtils.format("Cannot handle subquery: %s", dataSourceFromQuery));
}
return toolChest;
}
/**
* {@link QueryRunner} that on run builds a set of {@link QueryRunner} for a set of {@link SegmentDescriptor} and
* merges them using the {@link QueryToolChest}. The {@link VersionedIntervalTimeline} provides segment references,
* which are registered with a closer as they are acquired, and then released in the baggage of merged result
* {@link Sequence}
*/
public class ResourceManagingQueryRunner<T> implements QueryRunner<T>
{
private final VersionedIntervalTimeline<String, DataSegment> timeline;
private final QueryRunnerFactory<T, Query<T>> factory;
private final QueryToolChest<T, Query<T>> toolChest;
private final ExecutionVertex ev;
private final Iterable<SegmentDescriptor> specs;
View on GitHub (pinned to 9b90983fd2)