apache/beam · error · java.lang.UnsupportedOperationException

FULL OUTER JOIN is not supported when join a bounded table w

Error message

FULL OUTER JOIN is not supported when join a bounded table with an unbounded table.

What it means

BeamSideInputJoinRel handles joins between a bounded and an unbounded PCollection via side inputs, which only supports INNER, LEFT OUTER (unbounded left), and RIGHT OUTER (unbounded right) joins. A FULL OUTER JOIN in this bounded/unbounded mix cannot be materialized as a side input, so buildPTransform throws UnsupportedOperationException.

Source

Thrown at sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/impl/rel/BeamSideInputJoinRel.java:102

      RelTraitSet traitSet,
      RexNode conditionExpr,
      RelNode left,
      RelNode right,
      JoinRelType joinType,
      boolean semiJoinDone) {
    return new BeamSideInputJoinRel(
        getCluster(), traitSet, left, right, conditionExpr, variablesSet, joinType);
  }

  @Override
  public PTransform<PCollectionList<Row>, PCollection<Row>> buildPTransform() {
    // if one of the sides is Bounded & the other is Unbounded
    // then do a sideInput join.
    // When doing a sideInput join, the windowFn does not need to match.
    // Only support INNER JOIN & LEFT OUTER JOIN where left side of the join must be
    // the unbounded & RIGHT OUTER JOIN where right side of the join must be the unbounded
    if (joinType == JoinRelType.FULL) {
      throw new UnsupportedOperationException(
          "FULL OUTER JOIN is not supported when join "
              + "a bounded table with an unbounded table.");
    }

    BeamRelNode leftRelNode = BeamSqlRelUtils.getBeamRelInput(left);
    BeamRelNode rightRelNode = BeamSqlRelUtils.getBeamRelInput(right);

    if ((joinType == JoinRelType.LEFT && leftRelNode.isBounded() == PCollection.IsBounded.BOUNDED)
        || (joinType == JoinRelType.RIGHT
            && rightRelNode.isBounded() == PCollection.IsBounded.BOUNDED)) {
      throw new UnsupportedOperationException(
          String.format("%s side of an OUTER JOIN must be Unbounded table.", joinType.name()));
    }
    if (leftRelNode.isBounded() == IsBounded.UNBOUNDED
        && rightRelNode.isBounded() == IsBounded.UNBOUNDED) {
      throw new UnsupportedOperationException(
          "Side input join can only be used if one table is bounded.");
    }

View on GitHub (pinned to 12126d8942)

Solutions

  1. Rewrite as LEFT OUTER JOIN with the unbounded relation on the left (semantically equivalent orientation where possible)
  2. Union the unmatched sides manually: run an INNER side-input join plus separate filters for non-matching rows on each side, then UNION the results
  3. Make both sides bounded (batch mode) or both unbounded with compatible windows so a different join implementation is chosen

Example fix

// before
SELECT * FROM streaming_events FULL OUTER JOIN batch_dim ON streaming_events.k = batch_dim.k;
// after
SELECT s.*, d.* FROM streaming_events s LEFT OUTER JOIN batch_dim d ON s.k = d.k;
-- plus a separate query for unmatched dim rows if truly needed
Defensive patterns

Strategy: validation

Validate before calling

// Check boundedness + join type before submitting
if (joinType == JoinRelType.FULL && (isBounded(left) != isBounded(right))) {
  throw new IllegalArgumentException("FULL OUTER JOIN unsupported for bounded/unbounded mix; use LEFT OUTER");
}

Try / catch

try {
  result = sqlEnv.sqlQuery(q).evaluate();
} catch (UnsupportedOperationException e) {
  if (e.getMessage().contains("FULL OUTER JOIN is not supported")) {
    q = rewriteFullOuterToLeftOuterPlusUnion(q);
  } else throw e;
}

Prevention

When it happens

Trigger: A FULL OUTER JOIN SQL query where one side of the join is bounded (batch/BoundedSource) and the other is unbounded (streaming), causing the planner to select the side-input join implementation.

Common situations: Streaming-batch enrichment queries written as FULL OUTER JOIN; pipelines that switched one input from batch to streaming (or vice versa) after initially working.

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/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/57e21fba868bf737. Report an issue: GitHub.