{"record":{"id":"7e42272f4d35ad2c","repo":"apache/druid","slug":"union-datasource-with-non-leaf-inputs-is-not-suppo","errorCode":null,"errorMessage":"Union datasource with non-leaf inputs is not supported [%s]!","messagePattern":"Union datasource with non-leaf inputs is not supported \\[(.+?)\\]!","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/query/UnionDataSource.java","lineNumber":132,"sourceCode":"\n  @Override\n  public boolean isGlobal()\n  {\n    return dataSources.stream().allMatch(DataSource::isGlobal);\n  }\n\n  @Override\n  public boolean isProcessable()\n  {\n    return dataSources.stream().allMatch(DataSource::isProcessable);\n  }\n\n  @Override\n  public SegmentMapFunction createSegmentMapFunction(Query query)\n  {\n    for (DataSource dataSource : dataSources) {\n      if (!dataSource.getChildren().isEmpty()) {\n        throw new ISE(\"Union datasource with non-leaf inputs is not supported [%s]!\", dataSources);\n      }\n    }\n    return SegmentMapFunction.IDENTITY;\n  }\n\n  @Override\n  public byte[] getCacheKey()\n  {\n    return null;\n  }\n\n  @Override\n  public boolean equals(Object o)\n  {\n    if (this == o) {\n      return true;\n    }\n    if (o == null || getClass() != o.getClass()) {","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/UnionDataSource.java#L114-L150","documentation":"UnionDataSource.createSegmentMapFunction only supports unions of leaf datasources (typically tables); if any input still has children (e.g. a nested query or another wrapped datasource), Druid throws ISE because segment mapping for nested inputs is not implemented. Call Identity mapping only when every input is a leaf.","triggerScenarios":"Building a union whose members are themselves non-leaf datasources (queries, joins, nested unions) and then executing a query against it, hitting createSegmentMapFunction.","commonSituations":"SQL features generating unions of subqueries that were not flattened to tables; programmatic query building nesting datasources inside a union; older planner paths producing non-flattened unions.","solutions":["Flatten the query so union inputs are plain TableDataSources","Rewrite the query to avoid unions of subqueries (e.g. use UNION ALL at the SQL level with table inputs)","If building programmatically, extract base tables from nested datasources before wrapping in a union"],"exampleFix":"// before\nnew UnionDataSource(List.of(query1.getDataSource(), tableDs)); // query datasource has children\n// after\nList<DataSource> leaves = List.of(((QueryDataSource) query1.getDataSource()).getQuery().getDataSource(), tableDs);\nnew UnionDataSource(leaves); // all leaf","handlingStrategy":"validation","validationCode":"boolean allLeaf = unionDs.getChildren().stream().allMatch(d -> d.getChildren().isEmpty());\nif (!allLeaf) {\n  throw new UnsupportedOperationException(\"Union inputs must be leaf datasources\");\n}","typeGuard":"boolean isLeaf(DataSource ds) {\n  return ds.getChildren().isEmpty();\n}","tryCatchPattern":"try {\n  unionDs.createSegmentMapFunction(query);\n} catch (IllegalStateException e) {\n  // flatten the datasource tree before retrying\n}","preventionTips":["Flatten subquery datasources to tables before wrapping in a union","Check children of each union member when building unions programmatically"],"tags":["druid","datasource","union","unsupported-operation"],"backgroundTag":"unsupported-operation","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}