{"record":{"id":"e144fd0aa150faf7","repo":"apache/beam","slug":"found-multiple-methods-annotated-with-s-s-and-s","errorCode":null,"errorMessage":"Found multiple methods annotated with @%s. [%s] and [%s]","messagePattern":"Found multiple methods annotated with @(.+?)\\. \\[(.+?)\\] and \\[(.+?)\\]","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":56,"sourceCode":"    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)));\n      }\n    }\n\n    // Method must be public.\n    if ((first.getModifiers() & Modifier.PUBLIC) == 0) {\n      throw new IllegalArgumentException(\n          String.format(\"Method %s is not public.\", ReflectHelpers.formatMethod(first)));\n    }\n\n    return first;\n  }\n}\n","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/impl/ScalarFnReflector.java#L38-L74","documentation":"After collecting all @ApplyMethod-annotated methods of a ScalarFn, getApplyMethod allows multiple matches only if they are overrides of each other (same name and parameter types, inherited from parent classes). If two distinct annotated methods with different names or signatures are found, the invocation target is ambiguous and an IllegalArgumentException naming both methods is thrown.","triggerScenarios":"A ScalarFn class (or its superclass chain) declares two @ApplyMethod-annotated methods with different signatures or names — e.g. apply(String) and apply(String, Integer) both annotated, or two differently named methods both marked @ApplyMethod.","commonSituations":"Overloading the apply method and annotating every overload; adding a second apply for a new type during refactoring; a superclass and subclass each annotating genuinely different methods.","solutions":["Keep exactly one logical @ApplyMethod method per ScalarFn; annotate only the one you want invoked.","For overloads, remove @ApplyMethod from all but the target overload or split into separate ScalarFn classes.","If a parent and child override the same signature, that is allowed — make the signatures identical (same parameter types) so it is recognized as an override.","Search the class hierarchy (including superclasses) for stray @ApplyMethod annotations and remove them."],"exampleFix":"// before\n@ApplyMethod public String apply(String s) { ... }\n@ApplyMethod public String apply(String s, Integer n) { ... } // ambiguous\n// after\n@ApplyMethod public String apply(String s) { ... }\npublic String apply(String s, Integer n) { ... } // unannotated helper","handlingStrategy":"validation","validationCode":"static void checkSingleApplyMethod(ScalarFn fn) {\n  java.util.Set<String> sigs = new java.util.HashSet<>();\n  for (java.lang.reflect.Method m : fn.getClass().getMethods()) {\n    if (m.isAnnotationPresent(ScalarFn.ApplyMethod.class)\n        && !sigs.add(m.getName() + java.util.Arrays.toString(m.getParameterTypes())))\n      throw new IllegalStateException(\"multiple distinct @ApplyMethod methods in \" + fn.getClass());\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  ScalarFnReflector.getApplyMethod(scalarFn);\n} catch (IllegalArgumentException e) {\n  if (e.getMessage().startsWith(\"Found multiple methods\")) {\n    throw new IllegalStateException(\"Keep only one @ApplyMethod overload per ScalarFn\", e);\n  }\n  throw e;\n}","preventionTips":["Annotate only one overload of apply; leave helpers unannotated.","Split multi-signature UDFs into separate ScalarFn classes.","Search superclasses for stray @ApplyMethod annotations."],"tags":["java","reflection","annotation","udf","ambiguity"],"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"}