{"record":{"id":"754c9597251c14cc","repo":"apache/beam","slug":"unable-to-construct-ontimer-invoker-for-classname","errorCode":null,"errorMessage":"Unable to construct @OnTimer invoker for ${className}","messagePattern":"Unable to construct @OnTimer invoker for (.+?)","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/reflect/ByteBuddyOnTimerInvokerFactory.java","lineNumber":79,"sourceCode":"  @Override\n  public <InputT, OutputT> OnTimerInvoker<InputT, OutputT> forTimer(\n      DoFn<InputT, OutputT> fn, String timerId) {\n\n    @SuppressWarnings(\"unchecked\")\n    Class<? extends DoFn<?, ?>> fnClass = (Class<? extends DoFn<?, ?>>) fn.getClass();\n    try {\n      OnTimerMethodSpecifier onTimerMethodSpecifier =\n          OnTimerMethodSpecifier.forClassAndTimerId(fnClass, timerId);\n      Constructor<?> constructor = constructorCache.get(onTimerMethodSpecifier);\n\n      return (OnTimerInvoker<InputT, OutputT>) constructor.newInstance(fn);\n    } catch (InstantiationException\n        | IllegalAccessException\n        | IllegalArgumentException\n        | InvocationTargetException\n        | SecurityException\n        | ExecutionException e) {\n      throw new RuntimeException(\n          String.format(\n              \"Unable to construct @%s invoker for %s\",\n              OnTimer.class.getSimpleName(), fn.getClass().getName()),\n          e);\n    }\n  }\n\n  public <InputT, OutputT> OnTimerInvoker<InputT, OutputT> forTimerFamily(\n      DoFn<InputT, OutputT> fn, String timerId) {\n\n    @SuppressWarnings(\"unchecked\")\n    Class<? extends DoFn<?, ?>> fnClass = (Class<? extends DoFn<?, ?>>) fn.getClass();\n    try {\n      OnTimerMethodSpecifier onTimerMethodSpecifier =\n          OnTimerMethodSpecifier.forClassAndTimerId(fnClass, timerId);\n      Constructor<?> constructor = constructorTimerFamilyCache.get(onTimerMethodSpecifier);\n\n      return (OnTimerInvoker<InputT, OutputT>) constructor.newInstance(fn);","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/reflect/ByteBuddyOnTimerInvokerFactory.java#L61-L97","documentation":"ByteBuddyOnTimerInvokerFactory.forTimer generates and loads an invoker class for a DoFn's @OnTimer method via ByteBuddy, then reflectively instantiates it. Any failure during generation, classloading, constructor invocation, or the invoked method's static init (InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, ExecutionException) is rethrown as this RuntimeException with the DoFn class name. It means Beam could not produce the timer callback machinery for that specific DoFn.","triggerScenarios":"Calling forTimer (directly or through timer expiry dispatch) for a DoFn class whose generated invoker cannot be constructed: the DoFn is an inner/non-static class, has no accessible no-arg context, the generated class fails verification, or ByteBuddy generation fails for the class's visibility/package.","commonSituations":"Anonymous or non-static inner DoFn classes that cannot be instantiated reflectively; DoFn classes in restricted classloader contexts (Flink/Spark/Dataflow user-jar classloaders); JDK compatibility issues where ByteBuddy can't subclass/proxy under a newer JVM without the right ByteBuddy experimental flag.","solutions":["Make the DoFn a public static top-level class (not an inner/anonymous class) so the generated invoker can instantiate it.","Read the cause chain (getCause) to identify whether it's generation, classloading, or constructor failure, and fix that specific layer.","Ensure the JVM/ByteBuddy versions are compatible; set -Dnet.bytebuddy.experimental=true if running on a very new JDK.","Check classloader setup in the runner so Beam and the user jar can see each other's classes (e.g. child-first loading config)."],"exampleFix":"// before: anonymous inner DoFn\npipeline.apply(..., new DoFn<String, String>() { @OnTimer(\"t\") public void onTimer(OnTimerContext ctx) {...} });\n// after: public static class\npublic class MyDoFn extends DoFn<String, String> {\n  @OnTimer(\"t\")\n  public void onTimer(OnTimerContext ctx) { ... }\n}\npipeline.apply(..., ParDo.of(new MyDoFn()));","handlingStrategy":"try-catch","validationCode":"if (!Modifier.isPublic(myDoFn.getClass().getModifiers())\n    || myDoFn.getClass().isAnonymousClass()\n    || (myDoFn.getClass().isLocalClass() && !Modifier.isStatic(myDoFn.getClass().getModifiers()))) {\n  throw new IllegalStateException(\"DoFn must be a public top-level or static nested class\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  invoker = ByteBuddyOnTimerInvokerFactory.forTimer(fn, timerId);\n} catch (RuntimeException e) {\n  Throwable cause = e.getCause();\n  log.error(\"Failed to build @OnTimer invoker for \" + fn.getClass().getName()\n      + \"; cause=\" + cause, e);\n  throw e;\n}","preventionTips":["Declare DoFns as public static classes, never anonymous or inner classes","Give DoFns a public no-arg constructor when the runner instantiates them","Test timer paths on the target JVM/runner in CI","Keep JVM and Beam/ByteBuddy versions compatible; use net.bytebuddy.experimental for very new JDKs"],"tags":["java","reflection","bytecode","timers","apache-beam"],"backgroundTag":"class-not-found","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}