{"record":{"id":"45b238cf09b2641a","repo":"apache/druid","slug":"expected-2-children-got-d","errorCode":null,"errorMessage":"Expected [2] children, got [%d]","messagePattern":"Expected \\[2\\] children, got \\[(.+?)\\]","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/query/JoinDataSource.java","lineNumber":253,"sourceCode":"  }\n\n  @Nullable\n  public JoinableFactoryWrapper getJoinableFactoryWrapper()\n  {\n    return joinableFactoryWrapper;\n  }\n\n  @Override\n  public List<DataSource> getChildren()\n  {\n    return ImmutableList.of(left, right);\n  }\n\n  @Override\n  public DataSource withChildren(List<DataSource> children)\n  {\n    if (children.size() != 2) {\n      throw new IAE(\"Expected [2] children, got [%d]\", children.size());\n    }\n\n    return new JoinDataSource(\n        children.get(0),\n        children.get(1),\n        rightPrefix,\n        conditionAnalysis,\n        joinType,\n        leftFilter,\n        joinableFactoryWrapper,\n        joinAlgorithm\n    );\n  }\n\n  @Override\n  public boolean isCacheable(boolean isBroker)\n  {\n    return left.isCacheable(isBroker) && right.isCacheable(isBroker);","sourceCodeStart":235,"sourceCodeEnd":271,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/JoinDataSource.java#L235-L271","documentation":"JoinDataSource is a binary datasource combining left and right children with a join condition and prefix. Its withChildren implementation requires exactly two children; any other count throws this IllegalArgumentException to preserve the join tree's structure during planning/rewriting.","triggerScenarios":"Calling joinDataSource.withChildren(list) with a list whose size is not 2 — e.g. a rewriting pass that dropped a child after pushdown (leaving 1) or merged extra sources (3+).","commonSituations":"Custom datasource-rewriting code; predicate/condition pushdown passes that accidentally removed one side of the join; utilities rebuilding a tree with filtered children counts.","solutions":["Always pass exactly [leftChild, rightChild] to withChildren in the original order.","Fix the rewriting pass that changed the children count; if one side becomes empty, replace the join with a FilteredDataSource instead of calling withChildren with 1 child.","Log/inspect the children list before the call to confirm it originates from the same join node."],"exampleFix":"// before\njoinDs.withChildren(Collections.singletonList(newChild)); // size 1\n// after\njoinDs.withChildren(Arrays.asList(leftChild, newChild)); // keep both sides","handlingStrategy":"validation","validationCode":"if (children.size() != 2) {\n  throw new IllegalArgumentException(\"JoinDataSource expects exactly 2 children, got \" + children.size());\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Preserve left/right child pairing in rewrite passes.","Convert degenerate joins (one side removed) to a different datasource type instead of calling withChildren.","Unit-test rewrites on query trees containing joins."],"tags":["query-planning","join","datasource","invalid-argument-value"],"backgroundTag":"missing-required-argument","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"}