apache/druid · error · IllegalArgumentException

'clauses' and 'baseFilter' are both empty, no need to…

Error message

'clauses' and 'baseFilter' are both empty, no need to create HashJoinSegment

What it means

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.

Solutions

  1. Check clauses/baseFilter before constructing; use the base segment directly when both are empty
  2. Upgrade Druid if this arises inside the join planner (fixed versions guard the optimization path)
  3. Fix custom code that builds HashJoinSegment without verifying inputs

Example fix

// before
return new HashJoinSegment(base, null, Collections.emptyList(), preanalysis, ref);
// after
if (clauses.isEmpty() && baseFilter == null) { return base; }
return new HashJoinSegment(base, baseFilter, clauses, preanalysis, ref);
Defensive patterns

Strategy: validation

Validate before calling

if (clauses.isEmpty() && baseFilter == null) return baseSegment;

Try / catch

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; }

Prevention

When it happens

Trigger: Calling new HashJoinSegment(baseSegment, baseFilter=null, clauses=[], joinFilterPreAnalysis, ...) — typically from join machinery when filter-pushback and clause analysis removed everything.

Common situations: 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.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/39effe9b47bee58d. Report an issue: GitHub.

Appendix: source

Thrown at processing/src/main/java/org/apache/druid/segment/join/HashJoinSegment.java:86

   */
  public HashJoinSegment(
      Segment baseSegment,
      @Nullable Filter baseFilter,
      List<JoinableClause> clauses,
      JoinFilterPreAnalysis joinFilterPreAnalysis,
      Closeable referenceCloseable
  )
  {
    this.baseSegment = baseSegment;
    this.baseFilter = baseFilter;
    this.clauses = clauses;
    this.joinFilterPreAnalysis = joinFilterPreAnalysis;
    this.referenceCloseable = referenceCloseable;

    // Verify this virtual segment is doing something useful (otherwise it's a waste to create this object)
    if (clauses.isEmpty() && baseFilter == null) {
      CloseableUtils.closeAndWrapExceptions(this);
      throw new IAE("'clauses' and 'baseFilter' are both empty, no need to create HashJoinSegment");
    }
  }

  @Override
  public SegmentId getId()
  {
    return baseSegment.getId();
  }

  @Override
  public Interval getDataInterval()
  {
    // __time column will come from the baseSegment, so use its data interval.
    return baseSegment.getDataInterval();
  }

  @SuppressWarnings("unchecked")
  @Override

View on GitHub (pinned to 9b90983fd2)