{"record":{"id":"d3f18fe72c6a5724","repo":"quarkusio/quarkus","slug":"could-not-match-a-unique-method-name-methodname","errorCode":null,"errorMessage":"Could not match a unique method name '<methodName>' for bean named <beanName> with class <class> Offending expression is <expression> of @PreAuthorize on method '<methodName>' of class <class>","messagePattern":"Could not match a unique method name '<methodName>' for bean named <beanName> with class <class> Offending expression is <expression> of @PreAuthorize on method '<methodName>' of class <class>","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"extensions/spring-security/deployment/src/main/java/io/quarkus/spring/security/deployment/BeanMethodInvocationGenerator.java","lineNumber":274,"sourceCode":"        for (Type type : securedMethodInfo.parameterTypes()) {\n            sb.append(classDescOf(type).descriptorString());\n        }\n        sb.append(\")\");\n        return sb.toString();\n    }\n\n    private MethodInfo determineMatchingBeanMethod(String methodName, int methodParametersSize, ClassInfo beanClassInfo,\n            MethodInfo securedMethodInfo, String expression, String beanName) {\n        MethodInfo matchingBeanClassMethod = null;\n        for (MethodInfo candidateMethod : beanClassInfo.methods()) {\n            if (candidateMethod.name().equals(methodName) &&\n                    Modifier.isPublic(candidateMethod.flags()) &&\n                    DotNames.PRIMITIVE_BOOLEAN.equals(candidateMethod.returnType().name()) &&\n                    candidateMethod.parametersCount() == methodParametersSize) {\n                if (matchingBeanClassMethod == null) {\n                    matchingBeanClassMethod = candidateMethod;\n                } else {\n                    throw new IllegalArgumentException(\n                            \"Could not match a unique method name '\" + methodName + \"' for bean named \" + beanName\n                                    + \" with class \" + beanClassInfo.name() + \" Offending expression is \" +\n                                    expression + \" of @PreAuthorize on method '\" + methodName + \"' of class \"\n                                    + securedMethodInfo.declaringClass());\n                }\n            }\n        }\n        if (matchingBeanClassMethod == null) {\n            throw new IllegalArgumentException(\n                    \"Could not find a public, boolean returning method named '\" + methodName + \"' for bean named \" + beanName\n                            + \" with class \" + beanClassInfo.name() + \" Offending expression is \" +\n                            expression + \" of @PreAuthorize on method '\" + methodName + \"' of class \"\n                            + securedMethodInfo.declaringClass());\n        }\n        return matchingBeanClassMethod;\n    }\n\n}","sourceCodeStart":256,"sourceCodeEnd":292,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/spring-security/deployment/src/main/java/io/quarkus/spring/security/deployment/BeanMethodInvocationGenerator.java#L256-L292","documentation":"Given @PreAuthorize like @bean.check(#id), the processor must find the bean method by name (plus boolean return type and parameter count). If more than one method in the bean class matches name, return type, and parameter count, the match is ambiguous and the build fails because the processor cannot know which overload to invoke.","triggerScenarios":"A bean referenced in @PreAuthorize has two public boolean methods with the same name and same number of parameters (differing only in parameter types), and the SpEL expression does not disambiguate them.","commonSituations":"Overloaded authorization methods like canAccess(String, String) and canAccess(String, Long); refactorings that added overloads; generic type erasure producing identical erased signatures.","solutions":["Rename one of the overloaded bean methods and update the SpEL expression","Change parameter counts or signatures so exactly one candidate matches","Make one candidate non-public or change its boolean return type so it no longer matches","Collapse the overloads into a single method handling both cases"],"exampleFix":"// before: bean has canRead(String,String) and canRead(String,Long); SpEL: @authz.canRead(#id,#tenant)\n// after\npublic boolean canReadByIdAndTenant(String id, String tenant) // renamed; expression updated","handlingStrategy":"validation","validationCode":"List<Method> candidates = Arrays.stream(beanClass.getMethods())\n    .filter(m -> m.getName().equals(name) && m.getReturnType() == boolean.class\n        && m.getParameterCount() == paramCount).collect(Collectors.toList());\nif (candidates.size() != 1) {\n    throw new IllegalStateException(\"@PreAuthorize bean method not unique: \" + name);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Avoid overloading public boolean methods used in @PreAuthorize","Give security-check methods unique descriptive names","Add a startup test that exercises all @PreAuthorize expressions"],"tags":["spring-security","preauthorize","spel","overload","ambiguity","build-time"],"backgroundTag":"ambiguous-bean-method-overload","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}