apache/beam · error · java.lang.UnsupportedOperationException

Joining unbounded PCollections is currently only supported f

Error message

Joining unbounded PCollections is currently only supported for non-global windows with triggers that are known to produce output once per window,such as the default trigger with zero allowed lateness. In these cases Beam can guarantee it joins all input elements once per window. ${windowingStrategy} is not supported

What it means

For unbounded (streaming) PCollections, Beam's CoGBK-based SQL join can only guarantee that all elements of a window are seen together when windows are non-global and the trigger produces output exactly once per window (e.g. the default trigger with zero allowed lateness). verifySupportedTrigger checks each join input's WindowingStrategy and throws UnsupportedOperationException if streaming input does not meet this guarantee, since otherwise the join could emit partial/incorrect results.

Source

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

      try {
        leftWinFn.verifyCompatibility(rightWinFn);
      } catch (IncompatibleWindowException e) {
        throw new IllegalArgumentException(
            "WindowFns must match for a bounded-vs-bounded/unbounded-vs-unbounded join.", e);
      }

      verifySupportedTrigger(leftRows);
      verifySupportedTrigger(rightRows);

      return standardJoin(leftRows, rightRows, leftKeyFields, rightKeyFields);
    }
  }

  private <T> void verifySupportedTrigger(PCollection<T> pCollection) {
    WindowingStrategy windowingStrategy = pCollection.getWindowingStrategy();

    if (UNBOUNDED.equals(pCollection.isBounded()) && !triggersOncePerWindow(windowingStrategy)) {
      throw new UnsupportedOperationException(
          "Joining unbounded PCollections is currently only supported for "
              + "non-global windows with triggers that are known to produce output once per window,"
              + "such as the default trigger with zero allowed lateness. "
              + "In these cases Beam can guarantee it joins all input elements once per window. "
              + windowingStrategy
              + " is not supported");
    }
  }

  private boolean triggersOncePerWindow(WindowingStrategy windowingStrategy) {
    Trigger trigger = windowingStrategy.getTrigger();

    return !(windowingStrategy.getWindowFn() instanceof GlobalWindows)
        && trigger instanceof DefaultTrigger
        && ZERO.equals(windowingStrategy.getAllowedLateness());
  }

  private PCollection<Row> standardJoin(

View on GitHub (pinned to 12126d8942)

Solutions

  1. Use the default trigger with allowed lateness zero and a non-global window on both streaming inputs before the join
  2. Replace custom triggers (Repeatedly.forever, AfterFirst/AfterEach early firings) with the default trigger, or accumulate to fire once per window, on the join inputs
  3. If you need firing-on-update joins, implement the join manually with CoGroupByKey plus your own trigger semantics instead of the SQL join
  4. Switch to a temporal join pattern (e.g. window both sides with the same fixed/sliding windows and default trigger) so each window fires exactly once

Example fix

// before
stream.apply(Window.<Row>into(GlobalWindows()).triggering(Repeatedly.forever(AfterPane.elementCountAtLeast(100))).discardingFiredPanes());
// after
stream.apply(Window.<Row>into(FixedWindows.of(Duration.standardMinutes(1)))); // default trigger, zero allowed lateness
Defensive patterns

Strategy: validation

Validate before calling

static boolean joinableStreaming(PCollection<?> pc) {
  WindowingStrategy s = pc.getWindowingStrategy();
  return !GlobalWindows.INSTANCE.equals(s.getWindowFn())
      && s.getAllowedLateness().isZero()
      && DefaultTrigger.of().equals(s.getTrigger());
}

Try / catch

try {
  result = joinTables(streamA, streamB);
} catch (UnsupportedOperationException e) {
  if (e.getMessage().contains("Joining unbounded PCollections")) {
    // re-apply default trigger / non-global windows and retry
  } else { throw e; }
}

Prevention

When it happens

Trigger: Running a SQL JOIN where at least one input is an unbounded PCollection whose WindowingStrategy fails triggersOncePerWindow: global window on a stream, custom triggers (e.g. Repeatedly/Count/AfterEach), non-zero allowed lateness, or accumulating/retracting modes that fire multiple times per window.

Common situations: Joining two streaming tables where one has a custom early/late trigger configured; joining a stream in the global window (no Window.into applied) with another stream; streams with allowed lateness > 0 or discarded/accumulating fire-on-update triggers set for side inputs.

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