apache/beam · error · UnsupportedOperationException
CausedByDrain unsupported in %s
Error message
CausedByDrain unsupported in %s
What it means
This error is thrown by the default UnsupportedInvocationBehavior in DoFnInvoker when causedByDrain() is called from a context that cannot report drain causality. causedByDrain() tells an @OnTimer callback whether the timer fired because of pipeline draining (Drain job action) and is only meaningful on runners that implement drain semantics. Outside such a runner — or when the invoker lacks drain support — the call is rejected rather than returning a misleading false.
Source
Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/reflect/DoFnInvoker.java:372
throw new UnsupportedOperationException(
String.format("RecordOffset unsupported in %s", getErrorContext()));
}
@Override
public Instant fireTimestamp(DoFn<InputT, OutputT> doFn) {
throw new UnsupportedOperationException(
String.format("FireTimestamp unsupported in %s", getErrorContext()));
}
@Override
public CausedByDrain causedByDrain(DoFn<InputT, OutputT> doFn) {
throw new UnsupportedOperationException(
String.format("CausedByDrain unsupported in %s", getErrorContext()));
}
@Override
public ValueKind valueKind(DoFn<InputT, OutputT> doFn) {
throw new UnsupportedOperationException(
String.format("ValueKind unsupported in %s", getErrorContext()));
}
@Override
public String timerId(DoFn<InputT, OutputT> doFn) {
throw new UnsupportedOperationException(
String.format("TimerId unsupported in %s", getErrorContext()));
}
@Override
public TimeDomain timeDomain(DoFn<InputT, OutputT> doFn) {
throw new UnsupportedOperationException(
String.format("TimeDomain unsupported in %s", getErrorContext()));
}
@Override
public OutputReceiver<OutputT> outputReceiver(DoFn<InputT, OutputT> doFn) {
throw new UnsupportedOperationException(View on GitHub (pinned to 12126d8942)
Solutions
- Only call causedByDrain() from @OnTimer callbacks and only when targeting runners that support drain (e.g. Dataflow Streaming)
- Remove the check if drain semantics are irrelevant to your pipeline
- In tests, avoid asserting drain behavior via direct invoker usage; test drain paths with the actual runner or mock the context interface explicitly
- Wrap the call in try-catch for UnsupportedOperationException and treat it as not-caused-by-drain when unsupported
Example fix
// before
if (ctx.causedByDrain()) { flush(); }
// after
boolean drain;
try { drain = ctx.causedByDrain(); } catch (UnsupportedOperationException e) { drain = false; }
if (drain) { flush(); } Defensive patterns
Strategy: fallback
Validate before calling
// Only query drain causality in streaming @OnTimer callbacks on runners with drain support; // otherwise skip the check entirely.
Type guard
boolean safeCausedByDrain(DoFn.OnTimerContext ctx) {
try { return ctx.causedByDrain(); }
catch (UnsupportedOperationException e) { return false; }
} Try / catch
try {
if (context.causedByDrain()) { flushAndAcknowledgeDrain(); }
} catch (UnsupportedOperationException e) {
// runner does not support drain; continue normal processing
} Prevention
- Gate drain-handling code behind runner capability (e.g. only Dataflow Streaming drains jobs)
- Never call causedByDrain() from @ProcessElement — it is timer-context only
- Don't rely on drain detection in unit tests; test drain behavior on the actual runner
When it happens
Trigger: Invoking context.causedByDrain() from an @OnTimer callback while the DoFn runs under an invoker with UnsupportedInvocationBehavior (direct DoFnInvokers.invokerFor(...) usage, unit-test harness, or a runner without drain support).
Common situations: Unit-testing a timer callback that checks causedByDrain(); running on a runner or in a Batch mode where draining is not implemented; invoking the DoFn through a custom/legacy DoFnRunner that doesn't wire up the CausedByDrain value.
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
- TimerFamily unsupported in ${context}
- FireTimestamp unsupported in %s
- Cannot access fire timestamp outside of @OnTimer method.
- Cannot access timerId as parameter outside of @OnTimer metho
- Cannot access timerId as parameter outside of @OnTimer metho
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/8eb9aaf3844b6930.
Report an issue: GitHub.