{"record":{"id":"d031b96b4d4fe1b5","repo":"spring-projects/spring-framework","slug":"found-candidate-annotation-binding-variables-bu","errorCode":null,"errorMessage":"Found {} candidate annotation binding variables but only one potential argument binding slot","messagePattern":"Found (.+?) candidate annotation binding variables but only one potential argument binding slot","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscoverer.java","lineNumber":438,"sourceCode":"\t * Match the given list of extracted variable names to argument slots.\n\t */\n\tprivate void bindAnnotationsFromVarNames(List<String> varNames) {\n\t\tif (!varNames.isEmpty()) {\n\t\t\t// we have work to do...\n\t\t\tint numAnnotationSlots = countNumberOfUnboundAnnotationArguments();\n\t\t\tif (numAnnotationSlots > 1) {\n\t\t\t\tthrow new AmbiguousBindingException(\"Found \" + varNames.size() +\n\t\t\t\t\t\t\" potential annotation variable(s) and \" +\n\t\t\t\t\t\tnumAnnotationSlots + \" potential argument slots\");\n\t\t\t}\n\t\t\telse if (numAnnotationSlots == 1) {\n\t\t\t\tif (varNames.size() == 1) {\n\t\t\t\t\t// it's a match\n\t\t\t\t\tfindAndBind(Annotation.class, varNames.get(0));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// multiple candidate vars, but only one slot\n\t\t\t\t\tthrow new IllegalArgumentException(\"Found \" + varNames.size() +\n\t\t\t\t\t\t\t\" candidate annotation binding variables\" +\n\t\t\t\t\t\t\t\" but only one potential argument binding slot\");\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// no slots so presume those candidate vars were actually type names\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * If the token starts meets Java identifier conventions, it's in.\n\t */\n\tprivate @Nullable String maybeExtractVariableName(@Nullable String candidateToken) {\n\t\tif (AspectJProxyUtils.isVariableName(candidateToken)) {\n\t\t\treturn candidateToken;\n\t\t}\n\t\treturn null;","sourceCodeStart":420,"sourceCodeEnd":456,"githubUrl":"https://github.com/spring-projects/spring-framework/blob/69bf83ad716d0cfc4b0520a19b4d8b24c79d1538/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscoverer.java#L420-L456","documentation":"Thrown when the pointcut expression yields multiple annotation-binding variable names (candidate bindings from @annotation/@this/etc.) but the advice method has exactly one annotation-typed parameter slot. Spring cannot decide which candidate variable name to assign to the single slot. This is an IllegalArgumentException (not AmbiguousBindingException), signaling a structural mismatch between pointcut and advice.","triggerScenarios":"A pointcut like @annotation(a) && @annotation(b) (two binding variable names) with an advice method that has only one annotation-typed parameter — e.g. void before(MyAnnotation single). Only fires when setRaiseExceptions(true).","commonSituations":"Over-specifying annotation bindings in a pointcut when the advice only needs one, or pointcut designator arguments that look like variable names but are intended as type names.","solutions":["Remove extra annotation-binding expressions from the pointcut so only one candidate variable remains","Compile with -parameters to bypass name deduction entirely","Ensure pointcut annotation designator arguments are fully-qualified type names when they represent types, not bindings"],"exampleFix":"// before\n@Before(\"@annotation(a) && @this(b)\")\npublic void before(MyAnnotation single) { ... } // 2 candidates, 1 slot\n\n// after\n@Before(\"@annotation(a)\")\npublic void before(MyAnnotation a) { ... }","handlingStrategy":"validation","validationCode":"// Check if the pointcut has more annotation binding variable names than the\n// advice has annotation-typed params.\nint annotationSlots = (int) Arrays.stream(adviceMethod.getParameterTypes())\n    .filter(Annotation.class::isAssignableFrom).count();\n// Crude count of annotation designator bindings in the expression:\nlong designatorCount = Arrays.stream(new String[]{\"@annotation\",\"@this\",\"@target\",\"@within\",\"@withincode\"})\n    .filter(pointcutExpr::contains).count();\nif (annotationSlots == 1 && designatorCount > 1) {\n    throw new IllegalStateException(\"Multiple annotation designators but only one annotation param slot\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    discoverer.getParameterNames(adviceMethod);\n} catch (IllegalArgumentException ex) {\n    // Multiple annotation binding candidates for one slot — simplify the pointcut\n}","preventionTips":["Compile with -parameters","Keep annotation-binding designators in the pointcut to at most one when the advice has a single annotation param","Use fully-qualified type names in designator arguments when they are types, not bindings"],"tags":["spring-aop","aspectj","annotation-binding","parameter-binding"],"backgroundTag":null,"analyzedSha":"69bf83ad716d0cfc4b0520a19b4d8b24c79d1538","analyzedAt":"2026-08-09T15:32:58.770Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}