pathwaycom/pathway · error · ValueError

Universes of the arguments of Table.concat() have to be disj

Error message

Universes of the arguments of Table.concat() have to be disjoint.
Consider using Table.promise_universes_are_disjoint() to assert it.
(However, untrue assertion might result in runtime errors.)

What it means

Raised by Table._concat() when the universe solver cannot prove that the argument tables' row-id sets are pairwise disjoint. Vertical concatenation in Pathway is unsafe if two tables contain the same primary key, because the rows would collide. Pathway deliberately makes you assert disjointness explicitly.

Source

Thrown at python/pathway/internals/table.py:1675

            )
        id_type = _types_lca_with_error(
            *[arg.schema._id_dtype for arg in all_args],
            function_name="a concat",
            pointers=True,
        )

        return Table._concat(
            *[tab.cast_to_types(**schema).update_id_type(id_type) for tab in all_args]
        )

    @trace_user_frame
    @contextualized_operator
    def _concat(self, *others: Table[TSchema]) -> Table[TSchema]:
        union_ids = (self._id_column, *(other._id_column for other in others))
        if not self._get_universe_solver().query_are_disjoint(
            *(c.universe for c in union_ids)
        ):
            raise ValueError(
                "Universes of the arguments of Table.concat() have to be disjoint.\n"
                + "Consider using Table.promise_universes_are_disjoint() to assert it.\n"
                + "(However, untrue assertion might result in runtime errors.)"
            )
        context = clmn.ConcatUnsafeContext(
            union_ids=union_ids,
            updates=tuple(
                {col_name: other._columns[col_name] for col_name in self.keys()}
                for other in others
            ),
        )
        return self._table_with_context(context)

    @trace_user_frame
    @check_arg_types
    def update_cells(self, other: Table, _stacklevel: int = 1) -> Table:
        """Updates cells of `self`, breaking ties in favor of the values in `other`.

View on GitHub (pinned to fa2f74a464)

Solutions

  1. If the tables are provably disjoint, assert it: pw.Table.concat(t1.promise_universes_are_disjoint(), t2) — note the assertion only affects t2's relation to earlier args
  2. Regenerate ids on one side so they cannot collide: t2 = t2.with_id_from(...) or t2 = t2.with_id(pw.this.id + offset)
  3. If you actually want UNION-with-collision semantics, use update_cells/update_rows composition or a join-based merge instead of concat
  4. Verify overlap first with pw.debug.compute_and_print on both id sets in a small test

Example fix

# before
t3 = pw.Table.concat(t1, t2)  # ValueError: universes not disjoint

# after (only if truly disjoint!)
t3 = pw.Table.concat(t1, t2.promise_universes_are_disjoint())
# or force fresh ids
t3 = pw.Table.concat(t1, t2.with_id_from(pw.this.make_ptr(t2.id, t2.name)))
Defensive patterns

Strategy: validation

Validate before calling

# Only assert when ids provably cannot overlap (e.g. distinct key ranges)
def disjoint_concat(t1, t2):
    return pw.Table.concat(t1, t2.promise_universes_are_disjoint())

Try / catch

try:
    t3 = pw.Table.concat(t1, t2)
except ValueError as e:
    if 'have to be disjoint' in str(e):
        t3 = pw.Table.concat(t1, t2.with_id_from(
            pw.this.make_ptr(pw.this.id, 'source2')
        ))

Prevention

When it happens

Trigger: pw.Table.concat(t1, t2) where t1 and t2 may share ids (e.g. both derived from the same source, or ids generated from overlapping values); the solver returns query_are_disjoint(...) == False.

Common situations: Concatenating two streams derived from one upstream table (ids inherited); combining tables whose ids both come from pointer_from_string over overlapping keys; new users expecting SQL UNION-like semantics.

Related errors


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