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
- Only use OnTimerContext inside @OnTimer-annotated methods; for element processing use ProcessContext.
- In test code, invoke timer methods through a runner that simulates timer firing (e.g. TestStream with timer events) instead of calling onTimerContext manually.
- If writing harness-level code, construct the appropriate context subclass for each lifecycle phase rather than reusing onTimerContext.
- 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
- Use OnTimerContext only in @OnTimer methods
- In tests, drive timers via TestStream or a runner that simulates timer firing
- Do not cast ProcessContext to OnTimerContext speculatively
- When writing harness-level code, construct the context type matching the lifecycle phase
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
- Cannot access fire timestamp outside of @OnTimer method.
- Cannot access OnWindowExpirationContext outside of…
- Cannot access time domain outside of @ProcessTimer method.
- Cannot access timerId as parameter outside of @OnTimer…
- CausedByDrain unsupported in
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)