apache/beam · error · UnsupportedOperationException

Cannot access fire timestamp outside of @OnTimer method.

Error message

Cannot access fire timestamp outside of @OnTimer method.

What it means

In Apache Beam's Fn API Java harness, DoFnSupportT.fireTimestamp() is the accessor backing the TimeParam/Instant 'fire timestamp' available only while executing a @OnTimer (or timer-firing) callback. FnApiDoFnRunner's ProcessContext implementation does not track a fire timestamp for ordinary element processing, so any request for the fire timestamp outside a timer callback throws UnsupportedOperationException. This guards against reading timer state that is meaningless during normal bundle/element processing.

Source

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

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

    @Override
    public @Nullable String currentRecordId(DoFn<InputT, OutputT> doFn) {
      return currentRecordId();
    }

    @Override
    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;

View on GitHub (pinned to 12126d8942)

Solutions

  1. Move the fireTimestamp() call (or the Instant/timer-timestamp parameter) into a method annotated with @OnTimer only.
  2. If the timestamp is needed for elements, use the element's timestamp instead: c.timestamp() on the ProcessContext, which is valid inside @ProcessElement.
  3. If firing info must flow to non-timer code, capture fireTimestamp inside the @OnTimer method and pass it explicitly (e.g. as a method argument or state variable).
  4. Verify the method's annotations with DoFnSigatures/annotation checks so the harness binds timer parameters only for @OnTimer methods.

Example fix

// before
@ProcessElement
public void processElement(ProcessContext c) {
  Instant ts = c.fireTimestamp(); // throws
  ...
}
// after
@ProcessElement
public void processElement(ProcessContext c) {
  Instant ts = c.timestamp(); // element timestamp, always valid
  ...
}
@OnTimer("myTimer")
public void onTimer(OnTimerContext c) {
  Instant fireTs = c.fireTimestamp(); // valid here
  ...
}
Defensive patterns

Strategy: validation

Validate before calling

// Before reading a timer-only value, confirm you are in a timer callback:
boolean inOnTimer = getClass().getEnclosingMethod() != null
    && getClass().getEnclosingMethod().isAnnotationPresent(OnTimer.class);
if (!inOnTimer) {
  Instant ts = processContext.timestamp(); // element timestamp instead of fireTimestamp()
}

Type guard

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

Try / catch

try {
  Instant fireTs = ((DoFn.OnTimerContext) c).fireTimestamp();
} catch (UnsupportedOperationException e) {
  // not in a @OnTimer callback; fall back to element timestamp
  Instant ts = ((DoFn.ProcessContext) c).timestamp();
}

Prevention

When it happens

Trigger: Calling doFnProcessContext.fireTimestamp() (or injecting an Instant via @Timestamp-style timer param resolution, e.g. DoFnSignatures requesting TimeDomain/Instant params) from a @ProcessElement, @StartBundle, or @FinishBundle method instead of from inside a @OnTimer method. Also occurs when DoFn signature inspection wires fireTimestamp into a non-timer invocation path.

Common situations: A developer refactors timer-handling code and reuses a helper that reads fireTimestamp from the context in the element path; a DoFn declares a fire-timestamp parameter but the method is not annotated @OnTimer; copying timer logic into @ProcessElement while migrating from the classic runner to the portable Fn API harness.

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/87e705e8b2227a0b. Report an issue: GitHub.