{"record":{"id":"2c2da3ace3efc72a","repo":"apache/beam","slug":"no-method-annotated-with-s-found-in-class-s","errorCode":null,"errorMessage":"No method annotated with @%s found in class %s.","messagePattern":"No method annotated with @(.+?) found in class (.+?)\\.","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/impl/ScalarFnReflector.java","lineNumber":43,"sourceCode":"import org.apache.beam.sdk.util.common.ReflectHelpers;\n\n/** Reflection-based implementation logic for {@link ScalarFn}. */\npublic class ScalarFnReflector {\n  /**\n   * Gets the method annotated with {@link\n   * org.apache.beam.sdk.extensions.sql.udf.ScalarFn.ApplyMethod} from {@code scalarFn}.\n   *\n   * <p>There must be exactly one method annotated with {@link\n   * org.apache.beam.sdk.extensions.sql.udf.ScalarFn.ApplyMethod}, and it must be public.\n   */\n  public static Method getApplyMethod(ScalarFn scalarFn) {\n    Class<? extends ScalarFn> clazz = scalarFn.getClass();\n    Collection<Method> matches =\n        ReflectHelpers.declaredMethodsWithAnnotation(\n            ScalarFn.ApplyMethod.class, clazz, ScalarFn.class);\n\n    if (matches.isEmpty()) {\n      throw new IllegalArgumentException(\n          String.format(\n              \"No method annotated with @%s found in class %s.\",\n              ScalarFn.ApplyMethod.class.getSimpleName(), clazz.getName()));\n    }\n\n    // If we have at least one match, then either it should be the only match\n    // or it should be an extension of the other matches (which came from parent\n    // classes).\n    Method first = matches.iterator().next();\n    for (Method other : matches) {\n      if (!first.getName().equals(other.getName())\n          || !Arrays.equals(first.getParameterTypes(), other.getParameterTypes())) {\n        throw new IllegalArgumentException(\n            String.format(\n                \"Found multiple methods annotated with @%s. [%s] and [%s]\",\n                ScalarFn.ApplyMethod.class.getSimpleName(),\n                ReflectHelpers.formatMethod(first),\n                ReflectHelpers.formatMethod(other)));","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/impl/ScalarFnReflector.java#L25-L61","documentation":"ScalarFnReflector.getApplyMethod locates the method annotated with @ScalarFn.ApplyMethod inside a ScalarFn implementation (searching the class and its parents, stopping at ScalarFn itself) so Beam can invoke the UDF. If no method carries the annotation, the ScalarFn is structurally invalid and an IllegalArgumentException is thrown — a ScalarFn must define exactly one apply method.","triggerScenarios":"Passing a ScalarFn subclass that forgot to annotate its apply-style method with @ApplyMethod, or annotated a method in an unrelated helper class, to Beam SQL scalar function registration (ScalarFunctionImpl/create paths).","commonSituations":"Renaming a method and accidentally removing the annotation; copy-pasting a plain class as a ScalarFn; using a custom annotation instead of ScalarFn.ApplyMethod; defining the function body in a superclass above the ScalarFn boundary.","solutions":["Annotate exactly one public method with @ScalarFn.ApplyMethod in your ScalarFn subclass.","Ensure the annotated method lives in the ScalarFn class itself or a superclass of it (not a side helper class).","Verify you imported org.apache.beam.sdk.schemas.transforms...ScalarFn.ApplyMethod, not a same-named annotation from another library.","Remove competing annotation uses so exactly one apply method exists (see also the multiple-methods error)."],"exampleFix":"// before\npublic class Upper extends ScalarFn {\n  public String apply(String s) { return s.toUpperCase(); } // no annotation\n}\n// after\npublic class Upper extends ScalarFn {\n  @ApplyMethod\n  public String apply(String s) { return s.toUpperCase(); }\n}","handlingStrategy":"validation","validationCode":"static void checkScalarFn(ScalarFn fn) {\n  long n = java.util.Arrays.stream(fn.getClass().getMethods())\n      .filter(m -> m.isAnnotationPresent(ScalarFn.ApplyMethod.class)).count();\n  if (n == 0) throw new IllegalArgumentException(fn.getClass() + \" needs an @ApplyMethod\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  ScalarFnReflector.getApplyMethod(scalarFn);\n} catch (IllegalArgumentException e) {\n  throw new IllegalStateException(\"ScalarFn missing @ApplyMethod: \" + scalarFn.getClass().getName(), e);\n}","preventionTips":["Always import the correct ScalarFn.ApplyMethod annotation.","Add a unit test that calls ScalarFnReflector.getApplyMethod on every ScalarFn.","Avoid removing annotations during method renames/refactors."],"tags":["java","reflection","annotation","udf","beam-sql"],"backgroundTag":"missing-required-argument","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"}