apache/beam · error · UnsupportedOperationException

Cannot access timer in non-window observing context.

Error message

Cannot access timer in non-window observing context.

What it means

NonWindowObservingProcessBundleContextBase.timer(String) throws UnsupportedOperationException because timers are defined per (key, window); in a non-window-observing context there is no window and thus no timer to create. The harness intentionally rejects timer creation here.

Source

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

      throw new UnsupportedOperationException(
          "Cannot access sideInput in non-window observing context.");
    }

    @Override
    public <T> T sideInput(PCollectionView<T> view) {
      throw new UnsupportedOperationException(
          "Cannot access sideInput in non-window observing context.");
    }

    @Override
    public State state(String stateId, boolean alwaysFetched) {
      throw new UnsupportedOperationException(
          "Cannot access state in non-window observing context.");
    }

    @Override
    public org.apache.beam.sdk.state.Timer timer(String timerId) {
      throw new UnsupportedOperationException(
          "Cannot access timer in non-window observing context.");
    }

    @Override
    public TimerMap timerFamily(String timerFamilyId) {
      throw new UnsupportedOperationException(
          "Cannot access timerFamily in non-window observing context.");
    }
  }

  /** Base implementation that does not override methods which need to be window aware. */
  private abstract class ProcessBundleContextBase extends DoFn<InputT, OutputT>.ProcessContext
      implements DoFnInvoker.ArgumentProvider<InputT, OutputT>, OutputReceiver<OutputT> {

    private ProcessBundleContextBase() {
      doFn.super();
    }

View on GitHub (pinned to 12126d8942)

Solutions

  1. Add a BoundedWindow parameter to the @ProcessElement method so the timer-capable (window-observing) context is used.
  2. Set timers only from @ProcessElement on keyed collections; declare the timer with @TimerId and @TimerFamily annotations.
  3. If you need time-based work without windowing, consider using a GlobalWindow with window-observing signature or restructure processing.

Example fix

// before
@ProcessElement
public void processElement(ProcessContext c) {
  c.timer(myTimerId).set(c.timestamp().plus(Duration.standardMinutes(1))); // throws
}

// after
@ProcessElement
public void processElement(ProcessContext c, BoundedWindow window) {
  c.timer(myTimerId).set(c.timestamp().plus(Duration.standardMinutes(1)));
}
Defensive patterns

Strategy: validation

Validate before calling

// Timer creation requires window-observing @ProcessElement:
boolean timerReady = Arrays.stream(dofn.getClass().getDeclaredMethods())
    .filter(m -> m.isAnnotationPresent(ProcessElement.class))
    .allMatch(m -> Arrays.asList(m.getParameterTypes()).contains(BoundedWindow.class));

Type guard

boolean canSetTimers(DoFn<?> doFn) {
  return Arrays.stream(doFn.getClass().getDeclaredMethods())
      .filter(m -> m.isAnnotationPresent(ProcessElement.class))
      .anyMatch(m -> Arrays.asList(m.getParameterTypes()).contains(BoundedWindow.class));
}

Try / catch

try {
  c.timer(myTimerId).set(target);
} catch (UnsupportedOperationException e) {
  throw new IllegalStateException("timer() requires a window-observing @ProcessElement; add BoundedWindow param", e);
}

Prevention

When it happens

Trigger: Calling c.timer(timerId) from a @ProcessElement method lacking a BoundedWindow parameter, or from @StartBundle/@FinishBundle methods.

Common situations: Adding event-time timers to a DoFn without adding the BoundedWindow parameter; trying to set timers during bundle start/finish; copying timer code from a windowed DoFn into a plain one.

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/8c08ce5076e989a4. Report an issue: GitHub.