apache/beam · error · IllegalStateException

GroupByKey cannot be applied to non-bounded PCollection in t

Error message

GroupByKey cannot be applied to non-bounded PCollection in the GlobalWindow without a trigger. Use a Window.into or Window.triggering transform prior to GroupByKey.

What it means

GroupByKey in the GlobalWindow with the DefaultTrigger never fires on an unbounded stream because the end-of-global-window watermark is never reached, so results would never be emitted. Beam rejects this configuration up front in applicableTo() with an IllegalStateException.

Source

Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/GroupByKey.java:180

  /**
   * For Beam internal use only. Tells runner that this is a GBK wrapped around of a
   * GroupByEncryptedKey
   */
  public boolean surroundsGBEK() {
    return this.surroundsGBEK;
  }

  /////////////////////////////////////////////////////////////////////////////

  public static void applicableTo(PCollection<?> input) {
    WindowingStrategy<?, ?> windowingStrategy = input.getWindowingStrategy();
    // Verify that the input PCollection is bounded, or that there is windowing/triggering being
    // used. Without this, the watermark (at end of global window) will never be reached.
    if (windowingStrategy.getWindowFn() instanceof GlobalWindows
        && windowingStrategy.getTrigger() instanceof DefaultTrigger
        && input.isBounded() != IsBounded.BOUNDED) {
      throw new IllegalStateException(
          "GroupByKey cannot be applied to non-bounded PCollection in the GlobalWindow without a"
              + " trigger. Use a Window.into or Window.triggering transform prior to GroupByKey.");
    }

    // Validate that the trigger does not finish before garbage collection time
    if (!triggerIsSafe(windowingStrategy)) {
      throw new IllegalArgumentException(
          String.format(
              "Unsafe trigger '%s' may lose data, did you mean to wrap it in"
                  + "`Repeatedly.forever(...)`?%nSee "
                  + "https://s.apache.org/finishing-triggers-drop-data "
                  + "for details.",
              windowingStrategy.getTrigger()));
    }
  }

  @Override
  public void validate(

View on GitHub (pinned to 12126d8942)

Solutions

  1. Add a windowing transform before GroupByKey, e.g. input.apply(Window.into(FixedWindows.of(Duration.standardMinutes(1))))
  2. Add a non-default triggering strategy, e.g. Window.triggering(Repeatedly.forever(AfterProcessingTime.pastFirstElementInPane().plusDelayOf(Duration.standardSeconds(30))))
  3. Ensure the source PCollection is truly bounded if you intended a batch pipeline (check the connector's isBounded)
  4. For streaming aggregation, consider GroupIntoBatches or Beam's streaming SQL/MBR alternatives that handle unbounded grouping

Example fix

// before
unboundedPCollection.apply(GroupByKey.create());
// after
unboundedPCollection
    .apply(Window.<KV<K,V>>into(FixedWindows.of(Duration.standardMinutes(1))))
    .apply(GroupByKey.create());
Defensive patterns

Strategy: validation

Validate before calling

if (input.isBounded() != IsBounded.BOUNDED
    && input.getWindowingStrategy().getWindowFn() instanceof GlobalWindows
    && input.getWindowingStrategy().getTrigger() instanceof DefaultTrigger) {
  throw new IllegalArgumentException("Apply Window.into/triggering before GroupByKey");
}

Try / catch

try {
  grouped = unbounded.apply(GroupByKey.create());
} catch (IllegalStateException e) {
  if (e.getMessage().contains("non-bounded PCollection in the GlobalWindow")) { /* add windowing */ }
  else throw e;
}

Prevention

When it happens

Trigger: Applying GroupByKey (directly, not inside GBK-into-Batches or with windowing) to an unbounded PCollection (streaming source such as Pub/Sub, Kafka, or an unbounded PTransform input) whose windowing strategy is GlobalWindows with the DefaultTrigger.

Common situations: Streaming pipelines that read from an unbounded source and apply GroupByKey without first calling Window.into(...); prototypes written batch-first and later repointed at a streaming source; forgetting fixed/sliding/session windows in a streaming job.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/33e3730f293d0bf6. Report an issue: GitHub.