{"record":{"id":"c8b8aade6ddb82ba","repo":"apache/beam","slug":"s-is-splittable-and-uses-timer-family-but-these-are-not","errorCode":null,"errorMessage":"%s is splittable and uses timer family, but these are not compatible","messagePattern":"(.+?) is splittable and uses timer family, 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":636,"sourceCode":"      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.\n   */\n  @Internal\n  public static DoFnSchemaInformation getDoFnSchemaInformation(\n      DoFn<?, ?> fn, PCollection<?> input) {\n    DoFnSignature signature = DoFnSignatures.getSignature(fn.getClass());\n    DoFnSignature.ProcessElementMethod processElementMethod = signature.processElement();\n    if (!processElementMethod.getSchemaElementParameters().isEmpty()) {\n      if (!input.hasSchema()) {\n        throw new IllegalArgumentException(\"Type of @Element must match the DoFn type\" + input);","sourceCodeStart":618,"sourceCodeEnd":654,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/ParDo.java#L618-L654","documentation":"ParDo.validate rejects DoFns that declare a TimerFamily while also being splittable — TimerFamily (multiple timers under one id with dynamic per-timer timestamps) is incompatible with element splitting in Beam's model. Construction fails with UnsupportedOperationException naming the offending DoFn class.","triggerScenarios":"ParDo.of(fn) where fn declares @TimerFamily fields and its @ProcessElement uses a RestrictionTracker (isSplittable).","commonSituations":"Using TimerFamily APIs (dynamic timer keys) inside a splittable source/reader DoFn; migrating timer code into an SDF.","solutions":["Move TimerFamily usage to a separate non-splittable DoFn applied after the splittable stage","Remove the TimerFamily declarations from the splittable DoFn","Use per-restriction checkpointing/resume instead of timer families for the splittable logic"],"exampleFix":"// before\nclass Sdf extends DoFn<T, O> { @TimerFamily \"tf\"; @ProcessElement ProcessContinuation processElement(RestrictionTracker r, TimerMap tm, ...) }\n// after\nclass Sdf extends DoFn<T, O> { @ProcessElement ProcessContinuation processElement(RestrictionTracker r, ...) } // TimerFamily moved downstream","handlingStrategy":"validation","validationCode":"DoFnSignature sig = DoFnSignatures.getSignature((Class) fn.getClass());\nif (!sig.timerFamilyDeclarations().isEmpty() && sig.processElement().isSplittable()) {\n  throw new IllegalArgumentException(fn.getClass() + \" cannot use TimerFamily and be splittable\");\n}","typeGuard":"boolean timerFamilyAndSplittable(DoFn<?, ?> fn) {\n  DoFnSignature sig = DoFnSignatures.getSignature((Class) fn.getClass());\n  return !sig.timerFamilyDeclarations().isEmpty() && 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 timer family, but these are not compatible\")) {\n    throw new IllegalStateException(\"Move TimerFamily usage downstream\", e);\n  }\n  throw e;\n}","preventionTips":["Keep TimerFamily out of splittable DoFns entirely","Separate splitting concerns (reading) from time-based logic (timers) into distinct stages","Run pipeline-construction tests in CI"],"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"}