apache/beam · error · UnsupportedOperationException

Cannot access time domain outside of @ProcessTimer method.

Error message

Cannot access time domain outside of @ProcessTimer method.

What it means

FnApiDoFnRunner.timeDomain() returns the TimeDomain (event time vs processing time) of the currently firing timer. Because timer firing is delivered via the runner's @ProcessTimer handling, this value only exists inside a timer callback; the accessor throws UnsupportedOperationException everywhere else. The message references @ProcessTimer, the internal runner entry point for fired timers (user code sees this as @OnTimer).

Solutions

  1. Access timeDomain() only from inside a @OnTimer method (OnTimerContext).
  2. If you need domain-dependent behavior for elements, decide it explicitly in your pipeline logic (you already chose the domain when setting the timer via Timer.withTimeDomain / in event-time vs processing-time timer creation).
  3. Split element-path and timer-path code so timer-only context parameters (TimeDomain, TimerId, fireTimestamp) are never reachable from the element path.
  4. Update helpers to take TimeDomain as an explicit parameter from the @OnTimer method.

Example fix

// before
@ProcessElement
public void processElement(ProcessContext c) {
  TimeDomain d = c.timeDomain(); // throws
}
// after
@OnTimer("tick")
public void onTimer(OnTimerContext c) {
  TimeDomain d = c.timeDomain(); // valid here
}
Defensive patterns

Strategy: validation

Validate before calling

TimeDomain domain = null;
if (c instanceof DoFn.OnTimerContext) {
  domain = ((DoFn.OnTimerContext) c).timeDomain();
} else {
  // element path: decide domain from your own timer configuration
  domain = myTimerIsProcessingTime ? TimeDomain.PROCESSING_TIME : TimeDomain.EVENT_TIME;
}

Type guard

static boolean canReadTimeDomain(DoFn.ProcessContext c) {
  return c instanceof DoFn.OnTimerContext;
}

Try / catch

try {
  TimeDomain d = ((DoFn.OnTimerContext) c).timeDomain();
} catch (UnsupportedOperationException e) {
  TimeDomain d = configuredDomain; // from Timer.withTimeDomain choice
}

Prevention

When it happens

Trigger: Calling context.timeDomain() (or binding a TimeDomain parameter) from @ProcessElement/@StartBundle/@FinishBundle instead of a @OnTimer method; timer-family parameter resolution wiring TimeDomain into a non-timer invocation inside the Fn API harness.

Common situations: Code migrated from runners where TimeDomain was accessible more broadly; generic context helper methods that read timeDomain unconditionally; mixing event-time and processing-time timer handling and probing the domain from the wrong callback.

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/869d69b34a65a6e7. Report an issue: GitHub.

Appendix: source

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

    public @Nullable Long currentRecordOffset(DoFn<InputT, OutputT> doFn) {
      return currentRecordOffset();
    }

    @Override
    public Instant fireTimestamp(DoFn<InputT, OutputT> doFn) {
      throw new UnsupportedOperationException(
          "Cannot access fire timestamp outside of @OnTimer method.");
    }

    @Override
    public String timerId(DoFn<InputT, OutputT> doFn) {
      throw new UnsupportedOperationException(
          "Cannot access timerId as parameter outside of @OnTimer method.");
    }

    @Override
    public TimeDomain timeDomain(DoFn<InputT, OutputT> doFn) {
      throw new UnsupportedOperationException(
          "Cannot access time domain outside of @ProcessTimer method.");
    }

    @Override
    public OutputReceiver<OutputT> outputReceiver(DoFn<InputT, OutputT> doFn) {
      return this;
    }

    @Override
    // OutputT == RestrictionT
    public void output(OutputT output) {
      OutputReceiver.super.output(output);
    }

    private final OutputReceiver<Row> mainRowOutputReceiver =
        mainOutputSchemaCoder == null
            ? null
            : new OutputReceiver<Row>() {

View on GitHub (pinned to 12126d8942)