apache/beam · error · UnsupportedOperationException

%s is splittable and uses timers, but these are not compatib

Error message

%s is splittable and uses timers, but these are not compatible

What it means

ParDo.validate rejects DoFns that use timers (via @TimerId or @TimerFamily declarations detected through signature.timerDeclarations()/timerFamilyDeclarations()) while being splittable, as timers conflict with the element-splitting semantics. The pipeline cannot be constructed.

Source

Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/ParDo.java:627

  /**
   * Perform common validations of the {@link DoFn}, for example ensuring that state is used
   * correctly and that its features can be supported.
   */
  private static <InputT, OutputT> void validate(DoFn<InputT, OutputT> fn) {
    DoFnSignature signature = DoFnSignatures.getSignature((Class) fn.getClass());

    // State is semantically incompatible with splitting
    if (!signature.stateDeclarations().isEmpty() && signature.processElement().isSplittable()) {
      throw new UnsupportedOperationException(
          String.format(
              "%s is splittable and uses state, but these are not compatible",
              fn.getClass().getName()));
    }

    // Timers are semantically incompatible with splitting
    if ((!signature.timerDeclarations().isEmpty() || !signature.timerFamilyDeclarations().isEmpty())
        && signature.processElement().isSplittable()) {
      throw new UnsupportedOperationException(
          String.format(
              "%s is splittable and uses timers, but these are not compatible",
              fn.getClass().getName()));
    }

    // TimerFamily is semantically incompatible with splitting
    if (!signature.timerFamilyDeclarations().isEmpty()
        && signature.processElement().isSplittable()) {
      throw new UnsupportedOperationException(
          String.format(
              "%s is splittable and uses timer family, but these are not compatible",
              fn.getClass().getName()));
    }
  }

  /**
   * Extract information on how the DoFn uses schemas. In particular, if the schema of an element
   * parameter does not match the input PCollection's schema, convert.

View on GitHub (pinned to 12126d8942)

Solutions

  1. Move timers into a separate, non-splittable stateful ParDo downstream of the splittable DoFn
  2. Remove the timer declarations from the splittable DoFn
  3. Replace timer-based logic with restriction/resume semantics native to SDFs (e.g. ProcessContinuation.resumedelayed)

Example fix

// before
class Sdf extends DoFn<T, O> { @TimerId "t"; @ProcessElement ProcessContinuation processElement(RestrictionTracker r, Timer t, ...) }
// after
class Sdf extends DoFn<T, O> { @ProcessElement ProcessContinuation processElement(RestrictionTracker r, ...) } // timers moved to downstream DoFn
Defensive patterns

Strategy: validation

Validate before calling

DoFnSignature sig = DoFnSignatures.getSignature((Class) fn.getClass());
if ((!sig.timerDeclarations().isEmpty() || !sig.timerFamilyDeclarations().isEmpty())
    && sig.processElement().isSplittable()) {
  throw new IllegalArgumentException(fn.getClass() + " cannot use timers and be splittable");
}

Type guard

boolean timersAndSplittable(DoFn<?, ?> fn) {
  DoFnSignature sig = DoFnSignatures.getSignature((Class) fn.getClass());
  return (!sig.timerDeclarations().isEmpty() || !sig.timerFamilyDeclarations().isEmpty())
      && sig.processElement().isSplittable();
}

Try / catch

try {
  pipeline.apply(ParDo.of(fn));
} catch (UnsupportedOperationException e) {
  if (e.getMessage() != null && e.getMessage().endsWith("splittable and uses timers, but these are not compatible")) {
    throw new IllegalStateException("Move timers to a downstream non-splittable DoFn", e);
  }
  throw e;
}

Prevention

When it happens

Trigger: ParDo.of(fn) where fn declares timer fields (@TimerId/@TimerFamily) and its @ProcessElement is splittable (uses a RestrictionTracker / returns ProcessContinuation).

Common situations: Adding event-time timers to a splittable file or streaming-source DoFn; porting a stateful/timer-based DoFn to an SDF pattern.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


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