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

  1. Obtain the invoker via DoFnInvokers.tryInvokeFor / newInvoker so the generated or reflective implementation overrides the accessor you need
  2. Only access the context type that matches the current lifecycle phase (FinishBundleContext only inside @FinishBundle)
  3. 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

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


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)