{"record":{"id":"39effe9b47bee58d","repo":"apache/druid","slug":"clauses-and-basefilter-are-both-empty-no-need","errorCode":null,"errorMessage":"'clauses' and 'baseFilter' are both empty, no need to create HashJoinSegment","messagePattern":"'clauses' and 'baseFilter' are both empty, no need to create HashJoinSegment","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/segment/join/HashJoinSegment.java","lineNumber":86,"sourceCode":"   */\n  public HashJoinSegment(\n      Segment baseSegment,\n      @Nullable Filter baseFilter,\n      List<JoinableClause> clauses,\n      JoinFilterPreAnalysis joinFilterPreAnalysis,\n      Closeable referenceCloseable\n  )\n  {\n    this.baseSegment = baseSegment;\n    this.baseFilter = baseFilter;\n    this.clauses = clauses;\n    this.joinFilterPreAnalysis = joinFilterPreAnalysis;\n    this.referenceCloseable = referenceCloseable;\n\n    // Verify this virtual segment is doing something useful (otherwise it's a waste to create this object)\n    if (clauses.isEmpty() && baseFilter == null) {\n      CloseableUtils.closeAndWrapExceptions(this);\n      throw new IAE(\"'clauses' and 'baseFilter' are both empty, no need to create HashJoinSegment\");\n    }\n  }\n\n  @Override\n  public SegmentId getId()\n  {\n    return baseSegment.getId();\n  }\n\n  @Override\n  public Interval getDataInterval()\n  {\n    // __time column will come from the baseSegment, so use its data interval.\n    return baseSegment.getDataInterval();\n  }\n\n  @SuppressWarnings(\"unchecked\")\n  @Override","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/segment/join/HashJoinSegment.java#L68-L104","documentation":"HashJoinSegment is a virtual segment that applies join clauses and/or a base filter on top of a base segment. Constructing it with both an empty clause list and a null base filter would produce a useless wrapper, so the constructor closes itself and throws this IAE.","triggerScenarios":"Calling new HashJoinSegment(baseSegment, baseFilter=null, clauses=[], joinFilterPreAnalysis, ...) — typically from join machinery when filter-pushback and clause analysis removed everything.","commonSituations":"Join planning code paths that unconditionally wrap segments even after optimizing away all clauses and filters; custom segment-wrapping code written against Druid's join APIs.","solutions":["Check clauses/baseFilter before constructing; use the base segment directly when both are empty","Upgrade Druid if this arises inside the join planner (fixed versions guard the optimization path)","Fix custom code that builds HashJoinSegment without verifying inputs"],"exampleFix":"// before\nreturn new HashJoinSegment(base, null, Collections.emptyList(), preanalysis, ref);\n// after\nif (clauses.isEmpty() && baseFilter == null) { return base; }\nreturn new HashJoinSegment(base, baseFilter, clauses, preanalysis, ref);","handlingStrategy":"validation","validationCode":"if (clauses.isEmpty() && baseFilter == null) return baseSegment;","typeGuard":null,"tryCatchPattern":"try { s = new HashJoinSegment(base, f, clauses, pre, ref); } catch (IAE e) { if (e.getMessage().contains(\"no need to create\")) s = base; else throw e; }","preventionTips":["Only wrap segments when joins/filters exist","Check clause analysis results before wrapping"],"tags":["join","segments","internal"],"backgroundTag":"invalid-constructor-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"}