apache/beam · error · UnsupportedOperationException

Cannot access StartBundleContext outside of @StartBundle met

Error message

Cannot access StartBundleContext outside of @StartBundle method.

What it means

The invoker context used outside of @StartBundle methods throws UnsupportedOperationException when startBundleContext(doFn) is requested, because the StartBundleContext (which can emit output with bundle lifecycle semantics) only exists while running a @StartBundle method. Any other DoFn lifecycle method receives a context whose startBundleContext() is intentionally unimplemented.

Source

Thrown at sdks/java/harness/src/main/java/org/apache/beam/fn/harness/FnApiDoFnRunner.java:2016

    }
  }

  /** Base implementation that does not override methods which need to be window aware. */
  private abstract class ProcessBundleContextBase extends DoFn<InputT, OutputT>.ProcessContext
      implements DoFnInvoker.ArgumentProvider<InputT, OutputT>, OutputReceiver<OutputT> {

    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();

View on GitHub (pinned to 12126d8942)

Solutions

  1. Only access startBundleContext inside a method annotated with @StartBundle; refactor code that needs it into that method.
  2. If writing a custom invoker/advice, branch on the current lifecycle phase before calling startBundleContext.
  3. For emitting elements from @ProcessElement, use processContext or the normal output methods instead.

Example fix

// before
@ProcessElement
public void processElement(StartBundleContext ctx, ProcessContext c) {
  ctx.output(mainTag, element); // wrong context, throws when accessed
}

// after
@StartBundle
public void startBundle(StartBundleContext ctx) {
  ctx.output(mainTag, bootstrapValue);
}
@ProcessElement
public void processElement(ProcessContext c) {
  c.output(element);
}
Defensive patterns

Strategy: validation

Validate before calling

// Only use StartBundleContext inside @StartBundle methods; validate at registration time:
for (Method m : dofn.getClass().getDeclaredMethods()) {
  if (m.getParameterCount() > 0
      && DoFn.StartBundleContext.class.isAssignableFrom(m.getParameterTypes()[0])
      && !m.isAnnotationPresent(StartBundle.class)) {
    throw new IllegalStateException(m + " takes StartBundleContext but is not @StartBundle");
  }
}

Type guard

boolean isStartBundleMethod(Method m) {
  return m.isAnnotationPresent(DoFn.StartBundle.class);
}

Try / catch

try {
  ctx.startBundleContext(doFn).output(tag, v);
} catch (UnsupportedOperationException e) {
  throw new IllegalStateException("startBundleContext is only valid inside @StartBundle", e);
}

Prevention

When it happens

Trigger: Calling invoker-context.startBundleContext(doFn) (directly or via DoFn API reflection) from @ProcessElement, @FinishBundle, @OnTimer, or @GetInitialRestriction-style methods.

Common situations: Custom DoFnInvoker.Advice implementations that unconditionally call startBundleContext(); generic framework code passing the context around and invoking all lifecycle accessors; unit-test harnesses driving a ProcessContext and calling startBundleContext.

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/3e9db903a9bf8fad. Report an issue: GitHub.