apache/beam · error · UnsupportedOperationException

Cannot access key as parameter outside of @OnTimer method.

Error message

Cannot access key as parameter outside of @OnTimer method.

What it means

The restartable-key parameter (Object key()) of an ArgumentProvider is only available inside @OnTimer methods, where Beam knows the timer's key. The default BaseArgumentProvider.key() always throws UnsupportedOperationException with this message because a key cannot be determined outside that context.

Solutions

  1. Restrict key() access (and RestartableHandler usage) to @OnTimer methods
  2. Remove the key parameter from non-timer DoFn methods — only @OnTimer/RestartableHandler methods may receive it
  3. In tests, override key() in a custom ArgumentProvider returning the expected key
  4. If in runner code, install a timer-aware ArgumentProvider when invoking @OnTimer methods

Example fix

// before
@ProcessElement
public void processElement(ProcessContext c, Object key) { ... }
// after
@OnTimer("myTimer")
public void onTimer(OnTimerContext c) {
  // key context only valid inside @OnTimer
}
Defensive patterns

Strategy: validation

Validate before calling

// key() is only valid for @OnTimer methods:
boolean isOnTimer = method.isAnnotationPresent(DoFn.OnTimer.class)
    || method.getDeclaringClass() == DoFn.RestartableHandler.class;
if (!isOnTimer) {
  throw new IllegalStateException("key parameter only allowed in @OnTimer methods");
}

Try / catch

try {
  Object key = args.key();
} catch (UnsupportedOperationException e) {
  throw new IllegalStateException("key is only accessible inside @OnTimer methods", e);
}

Prevention

When it happens

Trigger: Declaring a RestartableHandler-style method (or calling key()) from a @ProcessElement, @StartBundle, or @FinishBundle method whose ArgumentProvider only supports non-timer contexts; directly invoking key() on a default BaseArgumentProvider in tests.

Common situations: Confusing RestartableHandler.key() semantics by adding key parameters to @ProcessElement methods; tests exercising timer-adjacent code with a bare BaseArgumentProvider; custom runner code that invokes key() without a timer context.

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/38222f5f23c5331a. Report an issue: GitHub.

Appendix: source

Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/reflect/DoFnInvoker.java:318

   */
  @Internal
  abstract class BaseArgumentProvider<InputT, OutputT>
      implements ArgumentProvider<InputT, OutputT> {
    @Override
    public DoFn<InputT, OutputT>.ProcessContext processContext(DoFn<InputT, OutputT> doFn) {
      throw new UnsupportedOperationException(
          String.format("ProcessContext unsupported in %s", getErrorContext()));
    }

    @Override
    public InputT element(DoFn<InputT, OutputT> doFn) {
      throw new UnsupportedOperationException(
          String.format("Element unsupported in %s", getErrorContext()));
    }

    @Override
    public @Nullable Object key() {
      throw new UnsupportedOperationException(
          "Cannot access key as parameter outside of @OnTimer method.");
    }

    @Override
    public @Nullable Object sideInput(String tagId) {
      throw new UnsupportedOperationException(
          String.format("SideInput unsupported in %s", getErrorContext()));
    }

    @Override
    public TimerMap timerFamily(String tagId) {
      throw new UnsupportedOperationException(
          String.format("TimerFamily unsupported in %s", getErrorContext()));
    }

    @Override
    public @Nullable Object schemaElement(int index) {
      throw new UnsupportedOperationException(

View on GitHub (pinned to 12126d8942)