apache/beam · error · java.lang.UnsupportedOperationException

FULL OUTER JOIN is not supported when join a Seekable table…

Error message

FULL OUTER JOIN is not supported when join a Seekable table with a non Seekable table.

What it means

Beam SQL's side-input lookup join supports only INNER, LEFT OUTER (non-seekable on the left) and RIGHT OUTER (non-seekable on the right) joins between a Seekable table and a non-Seekable table. A FULL OUTER JOIN cannot be performed this way because it would require streaming the seekable side's unmatched rows, so the translation throws this UnsupportedOperationException.

Solutions

  1. Rewrite as LEFT OUTER JOIN with the non-seekable table on the left, plus a complementary RIGHT-OUTER-derived query for unmatched seekable rows, and UNION ALL the results.
  2. Change the join type to INNER or LEFT/RIGHT OUTER, whichever matches which side is seekable.
  3. Materialize the seekable table as a bounded PCollection and use a regular (non-lookup) join that supports FULL OUTER JOIN.
  4. Implement a custom join transform (e.g. CoGroupByKey-based) if FULL OUTER semantics over both sides are truly required.

Example fix

-- before
SELECT * FROM non_seekable FULL OUTER JOIN seekable_table ON non_seekable.k = seekable_table.k
-- after
SELECT * FROM non_seekable LEFT OUTER JOIN seekable_table ON non_seekable.k = seekable_table.k
Defensive patterns

Strategy: validation

Validate before calling

if (joinType == JoinRelType.FULL && (isSeekable(left) ^ isSeekable(right))) { throw new IllegalArgumentException("FULL OUTER JOIN unsupported for mixed Seekable/non-Seekable sides"); }

Type guard

boolean lookupJoinAllowed = !(joinType == JoinRelType.FULL && exactlyOneSeekable);

Try / catch

try { pipeline.apply(SqlTransform.query(sql)); } catch (UnsupportedOperationException e) { if (e.getMessage().contains("FULL OUTER JOIN is not supported")) { /* decompose into LEFT OUTER + anti-join UNION ALL */ } else { throw e; } }

Prevention

When it happens

Trigger: Translating a BeamSql FULL OUTER JOIN query where exactly one side is a SeekableTable and the side-input lookup join rule (BeamSideInputLookupJoinRel) is chosen.

Common situations: Attempting FULL OUTER JOIN against a seekable side-input table such as a lookup backed by a stateful/seekable DoFn (e.g. a JDBC or BigTable-backed SeekableTable) in a streaming pipeline.

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/769142b0478e80e4. Report an issue: GitHub.

Appendix: source

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

      RelOptCluster cluster,
      RelTraitSet traitSet,
      RelNode left,
      RelNode right,
      RexNode condition,
      Set<CorrelationId> variablesSet,
      JoinRelType joinType) {
    super(cluster, traitSet, left, right, condition, variablesSet, joinType);
  }

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

    if ((joinType == JoinRelType.LEFT && seekableInputIndex().get() == 0)
        || (joinType == JoinRelType.RIGHT && seekableInputIndex().get() == 1)) {
      throw new UnsupportedOperationException(
          String.format("%s side of an OUTER JOIN must be a non Seekable table.", joinType.name()));
    }
    return new SideInputLookupJoin();
  }

  private class SideInputLookupJoin extends PTransform<PCollectionList<Row>, PCollection<Row>> {

    @Override
    public PCollection<Row> expand(PCollectionList<Row> pinput) {
      Schema schema = CalciteUtils.toSchema(getRowType());

View on GitHub (pinned to 12126d8942)