apache/beam · error · UnsupportedOperationException

Cannot access OnTimerContext outside of @OnTimer methods.

Error message

Cannot access OnTimerContext outside of @OnTimer methods.

What it means

This accessor supplies a DoFn<InputT, OutputT>.OnTimerContext to user code. OnTimerContext only exists while the Fn API harness is dispatching a fired timer; the runner's context object is shared across all DoFn stages of a bundle, so requesting it at any other point throws UnsupportedOperationException. It prevents user code from treating an ordinary ProcessContext as a timer context.

Solutions

  1. Only use OnTimerContext inside @OnTimer-annotated methods; for element processing use ProcessContext.
  2. In test code, invoke timer methods through a runner that simulates timer firing (e.g. TestStream with timer events) instead of calling onTimerContext manually.
  3. If writing harness-level code, construct the appropriate context subclass for each lifecycle phase rather than reusing onTimerContext.
  4. Check for accidental casts/miswired context lookups if this surfaces from framework code rather than your DoFn.

Example fix

// before (test invoking DoFn directly)
c.onTimerContext(myDoFn).timerId(); // throws
// after
// run through a runner that dispatches @OnTimer:
// PCollection<?> out = p.apply("set timer", Create.of(...))
//     .apply(TestStream/trigger timers) so onTimer runs with a real OnTimerContext
Defensive patterns

Strategy: type-guard

Validate before calling

if (!(c instanceof DoFn.OnTimerContext)) {
  throw new IllegalStateException("onTimerContext requested outside @OnTimer");
}

Type guard

static <I, O> boolean isOnTimerContext(DoFn<I, O>.ProcessContext c) {
  return c instanceof DoFn.OnTimerContext;
}

Try / catch

try {
  DoFn.OnTimerContext tc = runner.onTimerContext(doFn);
  // use tc
} catch (UnsupportedOperationException e) {
  // wrong lifecycle phase: fall back to ProcessContext behavior
}

Prevention

When it happens

Trigger: Calling context.onTimerContext(doFn) (directly or via DoFn runner plumbing, e.g. StartBundleContext/FinishBundleContext/BelowWatermark paths that ask for an OnTimerContext) outside the execution of a @OnTimer method — during @ProcessElement, bundle start/finish, or window expiration.

Common situations: Custom runner/harness code or test harnesses invoking DoFnRunner methods with the wrong context kind; user code casting the ProcessContext and calling onTimerContext; test utilities that invoke DoFn methods reflectively without setting up a timer dispatch 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/1d8813f89869cd87. Report an issue: GitHub.

Appendix: source

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

    @Override
    public MultiOutputReceiver taggedOutputReceiver(DoFn<InputT, OutputT> doFn) {
      return taggedOutputReceiver;
    }

    @Override
    public BundleFinalizer bundleFinalizer() {
      return bundleFinalizer;
    }

    @Override
    public Object restriction() {
      return currentRestriction;
    }

    @Override
    public DoFn<InputT, OutputT>.OnTimerContext onTimerContext(DoFn<InputT, OutputT> doFn) {
      throw new UnsupportedOperationException(
          "Cannot access OnTimerContext outside of @OnTimer methods.");
    }

    @Override
    public DoFn<InputT, OutputT>.OnWindowExpirationContext onWindowExpirationContext(
        DoFn<InputT, OutputT> doFn) {
      throw new UnsupportedOperationException(
          "Cannot access OnWindowExpirationContext outside of @OnWindowExpiration methods.");
    }

    @Override
    public RestrictionTracker<?, ?> restrictionTracker() {
      return currentTracker;
    }

    @Override
    public PipelineOptions getPipelineOptions() {
      return pipelineOptions;

View on GitHub (pinned to 12126d8942)