{"record":{"id":"902a98718716a478","repo":"apache/beam","slug":"s-is-splittable-and-uses-timers-but-these-are-not-compatible","errorCode":null,"errorMessage":"%s is splittable and uses timers, but these are not compatible","messagePattern":"(.+?) is splittable and uses timers, but these are not compatible","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/ParDo.java","lineNumber":627,"sourceCode":"  /**\n   * Perform common validations of the {@link DoFn}, for example ensuring that state is used\n   * correctly and that its features can be supported.\n   */\n  private static <InputT, OutputT> void validate(DoFn<InputT, OutputT> fn) {\n    DoFnSignature signature = DoFnSignatures.getSignature((Class) fn.getClass());\n\n    // State is semantically incompatible with splitting\n    if (!signature.stateDeclarations().isEmpty() && signature.processElement().isSplittable()) {\n      throw new UnsupportedOperationException(\n          String.format(\n              \"%s is splittable and uses state, but these are not compatible\",\n              fn.getClass().getName()));\n    }\n\n    // Timers are semantically incompatible with splitting\n    if ((!signature.timerDeclarations().isEmpty() || !signature.timerFamilyDeclarations().isEmpty())\n        && signature.processElement().isSplittable()) {\n      throw new UnsupportedOperationException(\n          String.format(\n              \"%s is splittable and uses timers, but these are not compatible\",\n              fn.getClass().getName()));\n    }\n\n    // TimerFamily is semantically incompatible with splitting\n    if (!signature.timerFamilyDeclarations().isEmpty()\n        && signature.processElement().isSplittable()) {\n      throw new UnsupportedOperationException(\n          String.format(\n              \"%s is splittable and uses timer family, but these are not compatible\",\n              fn.getClass().getName()));\n    }\n  }\n\n  /**\n   * Extract information on how the DoFn uses schemas. In particular, if the schema of an element\n   * parameter does not match the input PCollection's schema, convert.","sourceCodeStart":609,"sourceCodeEnd":645,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/ParDo.java#L609-L645","documentation":"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.","triggerScenarios":"ParDo.of(fn) where fn declares timer fields (@TimerId/@TimerFamily) and its @ProcessElement is splittable (uses a RestrictionTracker / returns ProcessContinuation).","commonSituations":"Adding event-time timers to a splittable file or streaming-source DoFn; porting a stateful/timer-based DoFn to an SDF pattern.","solutions":["Move timers into a separate, non-splittable stateful ParDo downstream of the splittable DoFn","Remove the timer declarations from the splittable DoFn","Replace timer-based logic with restriction/resume semantics native to SDFs (e.g. ProcessContinuation.resumedelayed)"],"exampleFix":"// before\nclass Sdf extends DoFn<T, O> { @TimerId \"t\"; @ProcessElement ProcessContinuation processElement(RestrictionTracker r, Timer t, ...) }\n// after\nclass Sdf extends DoFn<T, O> { @ProcessElement ProcessContinuation processElement(RestrictionTracker r, ...) } // timers moved to downstream DoFn","handlingStrategy":"validation","validationCode":"DoFnSignature sig = DoFnSignatures.getSignature((Class) fn.getClass());\nif ((!sig.timerDeclarations().isEmpty() || !sig.timerFamilyDeclarations().isEmpty())\n    && sig.processElement().isSplittable()) {\n  throw new IllegalArgumentException(fn.getClass() + \" cannot use timers and be splittable\");\n}","typeGuard":"boolean timersAndSplittable(DoFn<?, ?> fn) {\n  DoFnSignature sig = DoFnSignatures.getSignature((Class) fn.getClass());\n  return (!sig.timerDeclarations().isEmpty() || !sig.timerFamilyDeclarations().isEmpty())\n      && sig.processElement().isSplittable();\n}","tryCatchPattern":"try {\n  pipeline.apply(ParDo.of(fn));\n} catch (UnsupportedOperationException e) {\n  if (e.getMessage() != null && e.getMessage().endsWith(\"splittable and uses timers, but these are not compatible\")) {\n    throw new IllegalStateException(\"Move timers to a downstream non-splittable DoFn\", e);\n  }\n  throw e;\n}","preventionTips":["Never add @TimerId to DoFns using RestrictionTracker","Isolate timer logic in dedicated stateful stages","Test pipeline construction in CI to surface validate() failures early"],"tags":["java","apache-beam","splittable-dofn","timers"],"backgroundTag":"conflicting-config-options","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}