{"record":{"id":"f1ba27fa7d6b9ba1","repo":"apache/druid","slug":"cannot-handle-subquery-s","errorCode":null,"errorMessage":"Cannot handle subquery: %s","messagePattern":"Cannot handle subquery: (.+?)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"server/src/main/java/org/apache/druid/segment/realtime/appenderator/SinkQuerySegmentWalker.java","lineNumber":196,"sourceCode":"\n    // Sanity check: make sure the query is based on the table we're meant to handle.\n    if (!ev.getBaseTableDataSource().getName().equals(dataSource)) {\n      throw new ISE(\"Cannot handle datasource: %s\", dataSourceFromQuery);\n    }\n\n    final QueryRunnerFactory<T, Query<T>> factory = conglomerate.findFactory(query);\n    if (factory == null) {\n      throw new ISE(\"Unknown query type[%s].\", query.getClass());\n    }\n\n    final QueryToolChest<T, Query<T>> toolChest = factory.getToolchest();\n    final boolean skipIncrementalSegment = query.context().getBoolean(CONTEXT_SKIP_INCREMENTAL_SEGMENT, false);\n    final AtomicLong cpuTimeAccumulator = new AtomicLong(0L);\n\n    // Make sure this query type can handle the subquery, if present.\n    if ((dataSourceFromQuery instanceof QueryDataSource)\n        && !toolChest.canPerformSubquery(((QueryDataSource) dataSourceFromQuery).getQuery())) {\n      throw new ISE(\"Cannot handle subquery: %s\", dataSourceFromQuery);\n    }\n\n    // segmentMapFn maps each base Segment into a joined Segment if necessary.\n    final SegmentMapFunction segmentMapFn = JvmUtils.safeAccumulateThreadCpuTime(\n        cpuTimeAccumulator,\n        () -> ev.createSegmentMapFunction(policyEnforcer)\n    );\n\n    // We compute the join cache key here itself so it doesn't need to be re-computed for every segment\n    final Optional<byte[]> cacheKeyPrefix = Optional.ofNullable(query.getDataSource().getCacheKey());\n\n    // We need to report data for each Sink all-or-nothing, which means we need to acquire references for all\n    // subsegments (FireHydrants) of a segment (Sink) at once. To ensure they are properly released even when a\n    // query fails or is canceled, we acquire *all* sink reference upfront, and release them all when the main\n    // QueryRunner returned by this method is closed. (We can't do the acquisition and releasing at the level of\n    // each FireHydrant's runner, since then it wouldn't be properly all-or-nothing on a per-Sink basis.)\n    final List<SinkSegmentReference> allSegmentReferences = new ArrayList<>();\n    // Distinct Sinks (Druid segments) actually queried by this node. Tracked separately from","sourceCodeStart":178,"sourceCodeEnd":214,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/server/src/main/java/org/apache/druid/segment/realtime/appenderator/SinkQuerySegmentWalker.java#L178-L214","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nnew TopNQueryBuilder...query(new TableDataSource(\"x\"))... but wrapping a scan as subquery\nGroupByQuery sub = new GroupByQuery.Builder()...build();\nQueryDataSource ds = new QueryDataSource(new ScanQuery...); // scan subquery not supported here\n\n// after\n// use a supported inner query type, or materialize results into an inline datasource\nInlineDataSource ds = InlineDataSource.fromIterable(results, rowSignature);","handlingStrategy":"validation","validationCode":"// Java: check subquery support before issuing\nif (dataSource instanceof QueryDataSource) {\n  QueryToolChest<?, ?> chest = factory.getToolchest();\n  if (!chest.canPerformSubquery(((QueryDataSource) dataSource).getQuery())) {\n    throw new IllegalArgumentException(\"Subquery not supported for this query type\");\n  }\n}","typeGuard":null,"tryCatchPattern":"try { runner.query(query) } catch (IllegalStateException e) { if (e.getMessage().startsWith(\"Cannot handle subquery\")) { /* rewrite query without subquery */ } else throw e; }","preventionTips":["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"],"tags":["druid","subquery","query-execution"],"backgroundTag":"unsupported-operation","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}