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
- 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.
- Precompute the expression in a subquery/CTE as a projected column, then join on that column.
- Apply the transformation on the right-hand side instead if only one side needs it.
- 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
- Never wrap join-key columns in functions or arithmetic in ON clauses.
- Precompute expressions into projected columns before joining.
- Keep join keys as raw columns on both sides where possible.
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
- Cannot handle constant condition: %s
- Cannot handle non-equijoin condition: %s
- Unknown
- BroadcastTablesTooLarge
- FrameTooLarge
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/2d17227054ee99aa.
Report an issue: GitHub.