apache/beam · error · UnsupportedOperationException
Cannot access timerFamily in non-window observing context.
Error message
Cannot access timerFamily in non-window observing context.
What it means
NonWindowObservingProcessBundleContextBase.timerFamily(String) throws UnsupportedOperationException because timer families, like individual timers, are scoped to a window; without a bound window in a non-window-observing context the harness cannot resolve them.
Source
Thrown at sdks/java/harness/src/main/java/org/apache/beam/fn/harness/FnApiDoFnRunner.java:1996
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();
}
@Override
public PaneInfo paneInfo(DoFn<InputT, OutputT> doFn) {
return pane();
}
@OverrideView on GitHub (pinned to 12126d8942)
Solutions
- Add a BoundedWindow parameter to the @ProcessElement signature so the window-observing context (which implements timerFamily) is used.
- Declare the timer family with @TimerId/@TimerFamily annotations and only access it inside @ProcessElement on a keyed PCollection.
- Replace family-based dynamic timer ids with individual declared @TimerId timers if only a fixed set is needed.
Example fix
// before
@ProcessElement
public void processElement(ProcessContext c) {
c.timerFamily("family").get("t1").set(...); // throws
}
// after
@ProcessElement
public void processElement(ProcessContext c, BoundedWindow window) {
c.timerFamily("family").get("t1").set(...);
} Defensive patterns
Strategy: validation
Validate before calling
// Timer families require a window-observing context; verify before using timerFamily():
boolean windowed = Arrays.stream(dofn.getClass().getDeclaredMethods())
.filter(m -> m.isAnnotationPresent(ProcessElement.class))
.anyMatch(m -> Arrays.asList(m.getParameterTypes()).contains(BoundedWindow.class));
if (!windowed) throw new IllegalStateException("timerFamily needs BoundedWindow in @ProcessElement"); Type guard
boolean hasTimerFamilySupport(DoFn<?> doFn) {
return Arrays.stream(doFn.getClass().getDeclaredFields())
.anyMatch(f -> f.isAnnotationPresent(TimerFamily.class))
&& Arrays.stream(doFn.getClass().getDeclaredMethods())
.filter(m -> m.isAnnotationPresent(ProcessElement.class))
.anyMatch(m -> Arrays.asList(m.getParameterTypes()).contains(BoundedWindow.class));
} Try / catch
try {
c.timerFamily("family").get("t1").set(t);
} catch (UnsupportedOperationException e) {
throw new IllegalStateException("timerFamily requires a window-observing @ProcessElement", e);
} Prevention
- Declare timer families via @TimerFamily/@TimerId annotations before use.
- Keep all timer access inside @ProcessElement with a BoundedWindow parameter.
- Use plain @TimerId timers when a fixed set suffices; reserve families for dynamic ids.
When it happens
Trigger: Calling c.timerFamily(timerFamilyId) (TimerMap creation) from a @ProcessElement method without a BoundedWindow parameter, or from @StartBundle/@FinishBundle.
Common situations: Migrating groups of timers to @TimerId/@TimerFamily declarations and calling timerFamily from non-window-observing code; setting dynamic timer families during bundle lifecycle methods.
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 timer in non-window observing context.
- Cannot access timerId as parameter outside of @OnTimer metho
- Not expected to access TimeDomain from @ProcessElement
- TimerFamily unsupported in ${context}
- FireTimestamp unsupported in %s
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/bc342b24683b6659.
Report an issue: GitHub.