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

  1. Only call causedByDrain() from @OnTimer callbacks and only when targeting runners that support drain (e.g. Dataflow Streaming)
  2. Remove the check if drain semantics are irrelevant to your pipeline
  3. In tests, avoid asserting drain behavior via direct invoker usage; test drain paths with the actual runner or mock the context interface explicitly
  4. 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

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


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/8eb9aaf3844b6930. Report an issue: GitHub.