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
- Use the default trigger with allowed lateness zero and a non-global window on both streaming inputs before the join
- Replace custom triggers (Repeatedly.forever, AfterFirst/AfterEach early firings) with the default trigger, or accumulate to fire once per window, on the join inputs
- If you need firing-on-update joins, implement the join manually with CoGroupByKey plus your own trigger semantics instead of the SQL join
- 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
- For streaming joins, use non-global windows with the default trigger and zero allowed lateness
- Avoid custom or repeated triggers on PCollections fed into SQL joins
- If you need early results, do the join manually with CoGroupByKey and your own trigger design
- Assert on each input's WindowingStrategy (window fn, trigger, allowed lateness) before building the join
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
- WindowFns must match for a bounded-vs-bounded/unbounded-vs-u
- GroupByKey cannot be applied to non-bounded PCollection in t
- TVFSlidingWindow does not support side input windows.
- Runner does not support draining.
- Cannot encode null window
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/cc118b2bc9a9e138.
Report an issue: GitHub.