apache/beam · error · IllegalStateException
Continuation of a OnceTrigger must be a OnceTrigger
Error message
Continuation of a OnceTrigger must be a OnceTrigger
What it means
OnceTrigger subclasses (AfterCount, AfterFirst, etc.) must maintain the invariant that their continuation — the trigger used after they fire — is itself a OnceTrigger, otherwise firing guarantees break. The OnceTrigger.getContinuationTrigger override checks the super-produced continuation and throws IllegalStateException if a subclass returned a non-OnceTrigger continuation.
Solutions
- Make the custom trigger's continuation a OnceTrigger subclass, e.g. AfterWatermark.pastEndOfWindow() or never()
- Wrap continuation logic in a custom OnceTrigger subclass rather than AfterAll/Repeatedly
- If the trigger genuinely can fire multiple times, extend Trigger directly instead of OnceTrigger
Example fix
// before
@Override protected Trigger getContinuationTrigger(List<Trigger> conts) {
return AfterAll.of(conts); // not a OnceTrigger -> IllegalStateException
}
// after
@Override protected Trigger getContinuationTrigger(List<Trigger> conts) {
return AfterWatermark.pastEndOfWindow();
} Defensive patterns
Strategy: validation
Validate before calling
Trigger cont = myOnceTrigger.getContinuationTrigger(); if (!(cont instanceof OnceTrigger)) throw new IllegalStateException("Custom OnceTrigger continuation must be a OnceTrigger"); Type guard
boolean validOnceTrigger(Trigger t) { return t instanceof OnceTrigger && t.getContinuationTrigger() instanceof OnceTrigger; } Try / catch
try { Trigger c = trigger.getContinuationTrigger(); } catch (IllegalStateException e) { LOG.error("Fix custom OnceTrigger continuation", e); } Prevention
- When extending OnceTrigger, return only OnceTrigger subclasses (never(), AfterWatermark.pastEndOfWindow(), AfterCount...)
- Run Beam's trigger continuation unit tests (testContinuation pattern) for custom triggers
- Extend plain Trigger if the trigger can fire more than once
When it happens
Trigger: Defining a custom OnceTrigger whose getContinuationTrigger(List) returns a plain Trigger (e.g. AfterAll or Repeatedly.forever) instead of a OnceTrigger subclass, then calling getContinuationTrigger().
Common situations: Custom trigger development in Beam pipelines; upgrading Beam where trigger semantics tightened; test code (testContinuation) exercising continuation triggers of custom triggers.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- ReshuffleTrigger should not be used outside of Reshuffle
- A function must be provided to convert the input type into…
- A PValue contained in
- A schema was provided without a data format (or viceversa)…
- AfterProcessingTime trigger set without a delay or…
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/30b2c8f2fe911af9.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/windowing/Trigger.java:253
* <p>Triggers that are guaranteed to fire at most once should extend {@link OnceTrigger} rather
* than the general {@link Trigger} class to indicate that behavior.
*/
@Internal
public abstract static class OnceTrigger extends Trigger {
protected OnceTrigger(List<Trigger> subTriggers) {
super(subTriggers);
}
@Override
public final boolean mayFinish() {
return true;
}
@Override
public final OnceTrigger getContinuationTrigger() {
Trigger continuation = super.getContinuationTrigger();
if (!(continuation instanceof OnceTrigger)) {
throw new IllegalStateException("Continuation of a OnceTrigger must be a OnceTrigger");
}
return (OnceTrigger) continuation;
}
}
}
View on GitHub (pinned to 12126d8942)