apache/druid · error · IllegalArgumentException

Cannot build hash-join matcher on non-key-based condition: %

Error message

Cannot build hash-join matcher on non-key-based condition: %s

What it means

Hash-join matching relies on the right-hand join column being one of the table's key columns, because only key columns have usable indexes for lookups. getIndex() validates the condition's right column is a key column and throws IAE otherwise.

Source

Thrown at processing/src/main/java/org/apache/druid/segment/join/table/IndexedTableJoinMatcher.java:148

    this.selectorFactory = selectorFactory != null
                           ? selectorFactory
                           : new IndexedTableColumnSelectorFactory(table, () -> currentRow, closer);

    if (remainderNeeded) {
      this.matchedRows = new IntRBTreeSet();
    } else {
      this.matchedRows = null;
    }

  }

  private static IndexedTable.Index getIndex(
      final IndexedTable table,
      final Equality condition
  )
  {
    if (!table.keyColumns().contains(condition.getRightColumn())) {
      throw new IAE("Cannot build hash-join matcher on non-key-based condition: %s", condition);
    }

    final int keyColumnNumber = table.rowSignature().indexOf(condition.getRightColumn());

    return table.columnIndex(keyColumnNumber);
  }

  private static ConditionMatcher makeConditionMatcher(
      final IndexedTable.Index index,
      final ColumnSelectorFactory selectorFactory,
      final Equality condition
  )
  {
    return ColumnProcessors.makeProcessor(
        condition.getLeftExpr(),
        index.keyType(),
        new ConditionMatcherFactory(index, condition.isIncludeNull()),
        selectorFactory

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Include the join column in the keyColumns list when building the IndexedTable (e.g. RowBasedIndexedTable builder .keyColumns(...))
  2. Rewrite the query so the join condition uses a column that is a declared key
  3. Verify table.keyColumns() covers all columns used on the right side of ON equalities

Example fix

// before
RowBasedIndexedTable.Builder.create(adapter, signature, "v1")
    .keyColumns(Set.of("id")).build(rows, keyColumnsCacheKey);
// after
RowBasedIndexedTable.Builder.create(adapter, signature, "v1")
    .keyColumns(Set.of("id", "joinKey")).build(rows, keyColumnsCacheKey);
Defensive patterns

Strategy: validation

Validate before calling

if (!table.keyColumns().contains(rightColumn)) { throw new IllegalArgumentException("join column must be a key column: " + rightColumn); }

Try / catch

try { matcher = new IndexedTableJoinMatcher(...); } catch (IAE e) { /* re-plan or add key column */ }

Prevention

When it happens

Trigger: Building an IndexedTableJoinMatcher where the Equality condition's right column (the table-side column) is not in table.keyColumns(), e.g. joining ON t1.a = table.nonKeyColumn.

Common situations: Query referencing a broadcast table column that was not declared as a key column when the IndexedTable was built; tables built without indexes on the join key; schema drift between the table definition and the query.

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


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