apache/beam · error · UnsupportedOperationException
Cannot access FinishBundleContext outside of @FinishBundle m
Error message
Cannot access FinishBundleContext outside of @FinishBundle method.
What it means
The invoker context used outside of @FinishBundle methods throws UnsupportedOperationException when finishBundleContext(doFn) is requested, because FinishBundleContext — which allows emitting final outputs at bundle completion — only exists while running a @FinishBundle method. Other lifecycle contexts deliberately reject this accessor.
Source
Thrown at sdks/java/harness/src/main/java/org/apache/beam/fn/harness/FnApiDoFnRunner.java:2023
private ProcessBundleContextBase() {
doFn.super();
}
@Override
public PaneInfo paneInfo(DoFn<InputT, OutputT> doFn) {
return pane();
}
@Override
public DoFn<InputT, OutputT>.StartBundleContext startBundleContext(DoFn<InputT, OutputT> doFn) {
throw new UnsupportedOperationException(
"Cannot access StartBundleContext outside of @StartBundle method.");
}
@Override
public DoFn<InputT, OutputT>.FinishBundleContext finishBundleContext(
DoFn<InputT, OutputT> doFn) {
throw new UnsupportedOperationException(
"Cannot access FinishBundleContext outside of @FinishBundle method.");
}
@Override
public DoFn<InputT, OutputT>.ProcessContext processContext(DoFn<InputT, OutputT> doFn) {
return this;
}
@Override
public InputT element(DoFn<InputT, OutputT> doFn) {
return element();
}
@Override
public Object key() {
throw new UnsupportedOperationException(
"Cannot access key as parameter outside of @OnTimer method.");
}View on GitHub (pinned to 12126d8942)
Solutions
- Only call finishBundleContext/doFn.finishBundle-style output inside a @FinishBundle method; move flushing logic there.
- Emit per-element results with the ProcessContext from @ProcessElement instead of deferring through the finish context.
- In custom invoker advice, guard the accessor with the lifecycle phase check before invoking.
Example fix
// before
@ProcessElement
public void processElement(ProcessContext c) {
finishBundleContext(...).output(tag, accumulated); // throws
}
// after
@FinishBundle
public void finishBundle(FinishBundleContext ctx) {
ctx.output(tag, accumulated, Instant.EPOCH, GlobalWindow.INSTANCE);
} Defensive patterns
Strategy: validation
Validate before calling
// Only use FinishBundleContext inside @FinishBundle methods:
for (Method m : dofn.getClass().getDeclaredMethods()) {
if (m.getParameterCount() > 0
&& DoFn.FinishBundleContext.class.isAssignableFrom(m.getParameterTypes()[0])
&& !m.isAnnotationPresent(FinishBundle.class)) {
throw new IllegalStateException(m + " takes FinishBundleContext but is not @FinishBundle");
}
} Type guard
boolean isFinishBundleMethod(Method m) {
return m.isAnnotationPresent(DoFn.FinishBundle.class);
} Try / catch
try {
ctx.finishBundleContext(doFn).output(tag, v, ts, window);
} catch (UnsupportedOperationException e) {
throw new IllegalStateException("finishBundleContext is only valid inside @FinishBundle", e);
} Prevention
- Reserve FinishBundleContext for @FinishBundle methods that flush accumulated results.
- Accumulate in instance fields during @ProcessElement, emit in @FinishBundle.
- Guard custom invoker advice against unconditional lifecycle context access.
When it happens
Trigger: Calling invoker-context.finishBundleContext(doFn) from @ProcessElement, @StartBundle, @OnTimer, or state/timer methods; custom DoFnInvoker advice invoking the accessor unconditionally.
Common situations: Trying to flush accumulated results to the output from within @ProcessElement via FinishBundleContext; generic framework or testing code that calls every context accessor; misunderstanding which context object each annotated method receives.
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
- Cannot access StartBundleContext outside of @StartBundle met
- Cannot access window in non-window observing context.
- Cannot access sideInput in non-window observing context.
- Cannot access state in non-window observing context.
- Cannot access timer in non-window observing context.
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/5ecf0e2797c1d1b5.
Report an issue: GitHub.