apache/druid · error · IllegalArgumentException

Cannot handle equality condition involving left-hand express

Error message

Cannot handle equality condition involving left-hand expression: %s

What it means

SortMergeJoinStageProcessor.validateCondition requires every equi-join equality to have a plain column identifier on the left-hand side. This IllegalArgumentException is thrown when the left side of an equality in the join condition is an arbitrary expression (e.g. f(x) = y), because the shuffle/partitioning keys can only be direct columns.

Source

Thrown at multi-stage-query/src/main/java/org/apache/druid/msq/querykit/common/SortMergeJoinStageProcessor.java:275

   * Validates that a join condition can be handled by this processor. Returns the condition if it can be handled.
   * Throws {@link IllegalArgumentException} if the condition cannot be handled.
   */
  public static JoinConditionAnalysis validateCondition(final JoinConditionAnalysis condition)
  {
    if (condition.isAlwaysTrue()) {
      return condition;
    }

    if (condition.isAlwaysFalse()) {
      throw new IAE("Cannot handle constant condition: %s", condition.getOriginalExpression());
    }

    if (condition.getNonEquiConditions().size() > 0) {
      throw new IAE("Cannot handle non-equijoin condition: %s", condition.getOriginalExpression());
    }

    if (condition.getEquiConditions().stream().anyMatch(c -> !c.getLeftExpr().isIdentifier())) {
      throw new IAE(
          "Cannot handle equality condition involving left-hand expression: %s",
          condition.getOriginalExpression()
      );
    }

    return condition;
  }

  /**
   * Validates that all signatures from {@link #collectAndReadPartitions(ExecutionContext)} are prefixed by the
   * provided {@code keyColumns}.
   */
  private static Int2ObjectMap<List<ReadableInput>> validateInputFrameSignatures(
      final Int2ObjectMap<List<ReadableInput>> inputsByPartition,
      final List<List<KeyColumn>> keyColumns
  )
  {
    for (List<ReadableInput> readableInputs : inputsByPartition.values()) {

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Rewrite the join so both sides of the equality are plain column identifiers, e.g. ON t1.name = t2.name, and apply the function to a pre-computed column.
  2. Precompute the expression in a subquery/CTE as a projected column, then join on that column.
  3. Apply the transformation on the right-hand side instead if only one side needs it.
  4. Use the native query engine with a join that supports expression keys if rewriting is impossible.

Example fix

// before
FROM t1 JOIN t2 ON LOWER(t1.name) = t2.name
// after
FROM (SELECT LOWER(name) AS name_lower, ... FROM t1) t1 JOIN t2 ON t1.name_lower = t2.name
Defensive patterns

Strategy: validation

Validate before calling

// Verify each equality's left side is an identifier before running via MSQ
const bad = joinCondition.equalities.filter(e => !/^[a-zA-Z_][\w]*/.test(e.left) || e.left.includes('('));
if (bad.length) throw new Error('Equi-join left expressions not supported: ' + bad.map(b => b.left));

Type guard

const isIdentifierExpr = (e) => e && e.kind === 'identifier';

Prevention

When it happens

Trigger: An MSQ join whose ON clause contains an equality like ON lower(t1.name) = t2.name or ON t1.a + 1 = t2.a, where the left operand of '=' is an expression rather than a column reference.

Common situations: Writing SQL that normalizes a column during the join (functions, casts, arithmetic on the left key); translating hand-written native queries where the left field of an equi-condition is an expression.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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