pathwaycom/pathway · error · ValueError

Cannot join table with itself. Use <table>.copy() as one of

Error message

Cannot join table with itself. Use <table>.copy() as one of the arguments of the join.

What it means

join() compares left == right and raises ValueError if both joinables are the same object, because a self-join would make column references on both sides ambiguous. The message tells you to pass <table>.copy() as one side so each side gets its own ColumnReference namespace.

Source

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

        )

        return (inner_table, final_mapping)

    @staticmethod
    def _table_join(
        left: Joinable,
        right: Joinable,
        *on: expr.ColumnExpression,
        mode: JoinMode,
        id: expr.ColumnReference | None = None,
        left_instance: expr.ColumnReference | None = None,
        right_instance: expr.ColumnReference | None = None,
        exact_match: bool = False,  # if True do not optionalize output columns even if other than inner join is used
        left_exactly_once: bool = False,
        right_exactly_once: bool = False,
    ) -> JoinResult:
        if left == right:
            raise ValueError(
                "Cannot join table with itself. Use <table>.copy() as one of the arguments of the join."
            )

        left_table, left_substitutions = left._substitutions()
        right_table, right_substitutions = right._substitutions()

        chained_join_desugaring = SubstitutionDesugaring(
            {**left_substitutions, **right_substitutions}
        )

        if id is not None:
            id = chained_join_desugaring.eval_expression(id)
            id_column = id._column
        else:
            id_column = None

        common_column_names: StableSet[str] = StableSet()
        if left_instance is not None and right_instance is not None:

View on GitHub (pinned to fa2f74a464)

Solutions

  1. Copy one side: right = t.copy() then t.join(right, t.k == right.k)
  2. Use two independently-built (even identical) connector/table objects if a copy is not desired

Example fix

# before
res = t.join(t, t.k == t.k)

# after
t_right = t.copy()
res = t.join(t_right, t.k == t_right.k)
Defensive patterns

Strategy: validation

Validate before calling

def safe_self_join(t, cond_builder):
    t2 = t.copy()
    return t.join(t2, cond_builder(t, t2))

# or pre-check: assert left is not right and left != right before calling join

Prevention

When it happens

Trigger: t.join(t, t.k == t.k) — literally passing the same Table object as both left and right (also via join_inner/join_left/join_right sugar).

Common situations: Self-joins for pair/key matching (e.g. finding rows sharing a key within one table); refactor renaming two variables that end up aliasing the same table object.

Related errors


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