pathwaycom/pathway · error · ValueError

join condition should be of form <left_table>.<column> == <r

Error message

join condition should be of form <left_table>.<column> == <right_table>.<column>

What it means

validate_shape enforces the structural contract of join conditions: cond must be a ColumnBinaryOpExpression using the == operator with a ColumnReference on each side (left, right). Anything else — a single column, <, <=, .isna(), a function call, chained comparisons — raises ValueError demanding the <left_table>.<column> == <right_table>.<column> form.

Source

Thrown at python/pathway/internals/joins.py:1131

            columns_mapping,
            left_table,
            right_table,
            left,
            right,
            substitution,
            common_column_names,
            mode,
        )


def validate_shape(cond: expr.ColumnExpression) -> expr.ColumnBinaryOpExpression:
    if (
        not isinstance(cond, expr.ColumnBinaryOpExpression)
        or cond._operator != op.eq
        or not isinstance(cond._left, expr.ColumnReference)
        or not isinstance(cond._right, expr.ColumnReference)
    ):
        raise ValueError(
            "join condition should be of form <left_table>.<column> == <right_table>.<column>"
        )
    return cond


def validate_join_condition(
    cond: expr.ColumnExpression, left: Table, right: Table
) -> tuple[expr.ColumnReference, expr.ColumnReference, expr.ColumnBinaryOpExpression]:
    cond = validate_shape(cond)
    try:
        eval_type(cond)
    except TypeError:
        raise TypeError(
            "Incompatible types in a join condition.\n"
            + f"The types are: {eval_type(cond._left)} and {eval_type(cond._right)}. "
            + "You might try casting the respective columns to Any type to circumvent this,"
            + " but this is most probably an error."
        )

View on GitHub (pinned to fa2f74a464)

Solutions

  1. Rewrite the condition as a single equality between two column references
  2. Pass multiple equality conditions as separate arguments: t1.join(t2, t1.a == t2.a, t1.c == t2.c)
  3. For non-equality logic, pre-filter tables or apply the predicate in a later select/with_columns step

Example fix

# before
res = t1.join(t2, (t1.a == t2.a) & (t1.c == t2.c))

# after
res = t1.join(t2, t1.a == t2.a, t1.c == t2.c)
Defensive patterns

Strategy: type-guard

Validate before calling

from pathway.internals import expr, op

def is_eq_of_refs(cond) -> bool:
    return (
        isinstance(cond, expr.ColumnBinaryOpExpression)
        and cond._operator == op.eq
        and isinstance(cond._left, expr.ColumnReference)
        and isinstance(cond._right, expr.ColumnReference)
    )

Type guard

from pathway.internals import expr, op

def is_valid_join_condition(cond) -> bool:
    return (
        isinstance(cond, expr.ColumnBinaryOpExpression)
        and cond._operator == op.eq
        and isinstance(cond._left, expr.ColumnReference)
        and isinstance(cond._right, expr.ColumnReference)
    )

Prevention

When it happens

Trigger: t1.join(t2, t1.a) (bare column); t1.join(t2, t1.a < t2.b); t1.join(t2, (t1.a == t2.a) & (t1.c == t2.c)) — multiple conditions must be passed as separate *on args, not combined with &.

Common situations: SQL/pandas merge habits with arbitrary boolean predicates; trying compound conditions with & instead of varargs; inequality joins (unsupported).

Related errors


AI-assisted analysis of pathwaycom/pathway@fa2f74a464 (2026-08-15). Data as JSON: /api/errors/bc1d2f7f071d6f93. Report an issue: GitHub.