{"record":{"id":"b2332982c70f003d","repo":"spring-projects/spring-framework","slug":"still-unbound-args-at-this-target-args-bi","errorCode":null,"errorMessage":"Still {} unbound args at this()/target()/args() binding stage, with no way to determine between them","messagePattern":"Still (.+?) unbound args at this\\(\\)/target\\(\\)/args\\(\\) binding stage, with no way to determine between them","errorType":"exception","errorClass":"AmbiguousBindingException","httpStatus":null,"severity":"error","filePath":"spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscoverer.java","lineNumber":483,"sourceCode":"\t\t\treturn;\n\t\t}\n\t\tString[] tokens = StringUtils.tokenizeToStringArray(argsSpec, \",\");\n\t\tfor (int i = 0; i < tokens.length; i++) {\n\t\t\ttokens[i] = tokens[i].strip();\n\t\t\tString varName = maybeExtractVariableName(tokens[i]);\n\t\t\tif (varName != null) {\n\t\t\t\tvarNames.add(varName);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Parse the string pointcut expression looking for this(), target() and args() expressions.\n\t * If we find one, try and extract a candidate variable name and bind it.\n\t */\n\tprivate void maybeBindThisOrTargetOrArgsFromPointcutExpression() {\n\t\tif (this.numberOfRemainingUnboundArguments > 1) {\n\t\t\tthrow new AmbiguousBindingException(\"Still \" + this.numberOfRemainingUnboundArguments +\n\t\t\t\t\t\" unbound args at this()/target()/args() binding stage, with no way to determine between them\");\n\t\t}\n\n\t\tList<String> varNames = new ArrayList<>();\n\t\tString[] tokens = StringUtils.tokenizeToStringArray(this.pointcutExpression, \" \");\n\t\tfor (int i = 0; i < tokens.length; i++) {\n\t\t\tif (tokens[i].equals(\"this\") ||\n\t\t\t\t\ttokens[i].startsWith(\"this(\") ||\n\t\t\t\t\ttokens[i].equals(\"target\") ||\n\t\t\t\t\ttokens[i].startsWith(\"target(\")) {\n\t\t\t\tPointcutBody body = getPointcutBody(tokens, i);\n\t\t\t\ti += body.numTokensConsumed;\n\t\t\t\tString varName = maybeExtractVariableName(body.text);\n\t\t\t\tif (varName != null) {\n\t\t\t\t\tvarNames.add(varName);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (tokens[i].equals(\"args\") || tokens[i].startsWith(\"args(\")) {","sourceCodeStart":465,"sourceCodeEnd":501,"githubUrl":"https://github.com/spring-projects/spring-framework/blob/69bf83ad716d0cfc4b0520a19b4d8b24c79d1538/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscoverer.java#L465-L501","documentation":"Thrown by AspectJAdviceParameterNameDiscoverer at the this()/target()/args() binding stage when more than one argument remains unbound after all prior stages (JoinPoint, throwing, annotation, returning, primitive). The algorithm can bind at most one remaining slot from this()/target()/args(), so it cannot proceed with two or more unbound arguments.","triggerScenarios":"An advice method with two or more unbound parameters after prior binding stages whose pointcut uses this()/target()/args() designators — e.g. @Before(\"execution(* *(..)) && target(t)\") void before(Object t, Object other). Only fires when setRaiseExceptions(true).","commonSituations":"Advice methods with more parameters than the pointcut can bind, especially compiled without -parameters.","solutions":["Compile with -parameters so real parameter names are available","Add explicit bindings (this/target/args) in the pointcut for each remaining parameter","Reduce the advice method parameters so at most one is unbound by this stage"],"exampleFix":"// before\n@Before(\"execution(* *(..)) && target(t)\")\npublic void before(Object t, Object extra) { ... } // 2 unbound at this stage\n\n// after\n@Before(\"execution(* *(..)) && target(t) && args(extra)\")\npublic void before(Object t, Object extra) { ... } // both bound","handlingStrategy":"validation","validationCode":"// Count remaining unbound params after accounting for JoinPoint, returning, throwing\nint unbound = adviceMethod.getParameterCount();\nClass<?>[] types = adviceMethod.getParameterTypes();\nif (types.length > 0 && (types[0] == JoinPoint.class || types[0] == ProceedingJoinPoint.class)) unbound--;\nif (returningName != null) unbound--;\nif (throwingName != null) unbound--;\nif (unbound > 1) {\n    throw new IllegalStateException(unbound + \" params remain unbound; add -parameters or more bindings\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    discoverer.getParameterNames(adviceMethod);\n} catch (AspectJAdviceParameterNameDiscoverer.AmbiguousBindingException ex) {\n    // Too many unbound params — add explicit bindings or compile with -parameters\n}","preventionTips":["Compile with -parameters","Ensure each advice parameter is bound by a pointcut designator (this/target/args/annotation/returning/throwing)"],"tags":["spring-aop","aspectj","this-target-args","parameter-binding"],"backgroundTag":null,"analyzedSha":"69bf83ad716d0cfc4b0520a19b4d8b24c79d1538","analyzedAt":"2026-08-09T15:32:58.770Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}