apache/beam · error · UnsupportedOperationException
FinishBundleContext unsupported in
Error message
FinishBundleContext unsupported in %s
What it means
DoFnInvoker's anonymous 'unsupported invoker' base implementation throws UnsupportedOperationException for every context accessor. Asking it for a FinishBundleContext means the code path is being driven by the stub invoker rather than a real (generated or reflective) invoker bound to a runner. The message is formatted with getErrorContext(), which names the DoFn and call context that was being accessed.
Solutions
- Obtain the invoker via DoFnInvokers.tryInvokeFor / newInvoker so the generated or reflective implementation overrides the accessor you need
- Only access the context type that matches the current lifecycle phase (FinishBundleContext only inside @FinishBundle)
- Check which runner/test harness supplied the invoker; implement or wire the missing accessor override
Example fix
// before
DoFnInvoker<BaseDoFn> invoker = new DoFnInvoker.BaseDoFnInvoker<BaseDoFn, BaseDoFn>() {};
invoker.finishBundleContext(fn); // throws
// after
DoFnInvoker<BaseDoFn> invoker = DoFnInvokers.tryInvokeFor(fn);
// use the context only inside the @FinishBundle lifecycle invocation Defensive patterns
Strategy: validation
Validate before calling
if (!(invoker instanceof DoFnInvoker.BaseDoFnInvoker)) {
// safe to request lifecycle contexts
} Type guard
boolean isRealInvoker(DoFnInvoker<?, ?> i) {
return !(i instanceof DoFnInvoker.BaseDoFnInvoker);
} Try / catch
try {
ctx = invoker.finishBundleContext(doFn);
} catch (UnsupportedOperationException e) {
log.warn("Context unavailable: {}", e.getMessage());
return;
} Prevention
- Always create invokers via DoFnInvokers.tryInvokeFor/newInvoker
- Match context access to the lifecycle phase the DoFn is currently in
- In runner code, override every accessor you expect callers to use
When it happens
Trigger: Calling a method like doFnInvoker.onTimerContext(doFn) (or any other accessor on the same class) when the invoker instance is the 'unsupported' placeholder returned by the base class instead of a concrete invoker created via DoFnInvokers.
Common situations: Runner or testing harness code that constructs an invoker base without overriding the accessor; a custom runner reusing DoFnInvoker internals incorrectly; calling context accessors outside the lifecycle method that legitimately provides them (e.g. requesting a FinishBundleContext during processElement).
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 fire timestamp outside of @OnTimer method.
- Cannot access timerId as parameter outside of @OnTimer…
- CausedByDrain unsupported in
- FireTimestamp unsupported in
- Not expected to access Restriction from a regular DoFn in…
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/a7062dc038bfbbe0.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/reflect/DoFnInvoker.java:445
String.format("PipelineOptions unsupported in %s", getErrorContext()));
}
@Override
public DoFn<InputT, OutputT>.StartBundleContext startBundleContext(DoFn<InputT, OutputT> doFn) {
throw new UnsupportedOperationException(
String.format("StartBundleContext unsupported in %s", getErrorContext()));
}
@Override
public DoFn<InputT, OutputT>.FinishBundleContext finishBundleContext(
DoFn<InputT, OutputT> doFn) {
throw new UnsupportedOperationException(
String.format("FinishBundleContext unsupported in %s", getErrorContext()));
}
@Override
public DoFn<InputT, OutputT>.OnTimerContext onTimerContext(DoFn<InputT, OutputT> doFn) {
throw new UnsupportedOperationException(
String.format("OnTimerContext unsupported in %s", getErrorContext()));
}
@Override
public DoFn<InputT, OutputT>.OnWindowExpirationContext onWindowExpirationContext(
DoFn<InputT, OutputT> doFn) {
throw new UnsupportedOperationException(
String.format("OnWindowExpirationContext unsupported in %s", getErrorContext()));
}
@Override
public State state(String stateId, boolean alwaysFetched) {
throw new UnsupportedOperationException(
String.format("State unsupported in %s", getErrorContext()));
}
@Override
public Timer timer(String timerId) {View on GitHub (pinned to 12126d8942)