apache/druid · error · IllegalArgumentException
Cannot build hash-join matcher on non-equi-join condition
Error message
Cannot build hash-join matcher on non-equi-join condition: %s
What it means
The hash-join matcher can only evaluate equi-join conditions (Equality clauses on key columns). If the analysis of the join condition fails to extract at least one equality, building an IndexedTableJoinMatcher is impossible and the constructor throws IAE with the original expression.
Solutions
- Rewrite the join to use a simple equality on columns: ON t1.col = t2.col
- Move the non-equi predicate into a WHERE clause applied after the join
- Index the table's key columns so the equality can be matched (ColumnIndex for the join keys)
- Use a different join type (e.g. a subquery or sort-merge style join) instead of broadcast hash join
Example fix
// before // ON t1.startTime < t2.startTime // after // ON t1.id = t2.id WHERE t1.startTime < t2.startTime
Defensive patterns
Strategy: validation
Validate before calling
// ensure join condition is Equality before building the matcher
if (!(condition instanceof Equality)) { throw new IllegalArgumentException("hash join requires equi-join"); } Try / catch
try { new IndexedTableJoinMatcher(...); } catch (IAE e) { /* fall back to nested-loop/filter join */ } Prevention
- Write equi-join ON clauses (col = col) for broadcast joins
- Move inequality predicates to WHERE after the join
- Plan joins on indexed key columns
When it happens
Trigger: Passing a Joinable with a non-equi condition (e.g. inequality like t1.x < t2.y, or a complex expression such as lower(t1.a) = t2.b) into IndexedTableJoinMatcher construction via a hash join plan.
Common situations: SQL queries with inequality or expression-based join predicates against broadcast tables; Druid planner choosing a hash join when the predicate is not a simple column equality; version changes that tightened equi-join analysis.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Caching is not supported. Check `isCacheable` before…
- Cannot build hash-join matcher on non-key-based condition
- Cannot join lookup with condition referring to non-key…
- Cannot join lookup with non-equi condition
- Column[ ] is not a valid column for segment[ ]
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/3e9cbb14d3b49221.
Report an issue: GitHub.
Appendix: source
Thrown at processing/src/main/java/org/apache/druid/segment/join/table/IndexedTableJoinMatcher.java:123
this.singleRowMatching = false;
} else if (condition.isAlwaysFalse()) {
this.conditionMatchers = Collections.singletonList(() -> IntSortedSets.EMPTY_SET);
this.singleRowMatching = false;
} else if (condition.getNonEquiConditions().isEmpty()) {
final List<Pair<IndexedTable.Index, Equality>> indexes =
condition.getEquiConditions()
.stream()
.map(eq -> Pair.of(getIndex(table, eq), eq))
.collect(Collectors.toCollection(ArrayList::new));
this.conditionMatchers =
indexes.stream()
.map(pair -> makeConditionMatcher(pair.lhs, leftSelectorFactory, pair.rhs))
.collect(Collectors.toList());
this.singleRowMatching = indexes.stream().allMatch(pair -> pair.lhs.areKeysUnique(pair.rhs.isIncludeNull()));
} else {
throw new IAE(
"Cannot build hash-join matcher on non-equi-join condition: %s",
condition.getOriginalExpression()
);
}
ColumnSelectorFactory selectorFactory = table.makeColumnSelectorFactory(joinableOffset, closer);
this.selectorFactory = selectorFactory != null
? selectorFactory
: new IndexedTableColumnSelectorFactory(table, () -> currentRow, closer);
if (remainderNeeded) {
this.matchedRows = new IntRBTreeSet();
} else {
this.matchedRows = null;
}
}
View on GitHub (pinned to 9b90983fd2)