{"id":"d3eacffee1a45620","repo":"spring-projects/spring-framework","slug":"required-to-bind-arguments-but-only-bound","errorCode":null,"errorMessage":"Required to bind {} arguments, but only bound {} (JoinPointMatch {} bound in invocation)","messagePattern":"Required to bind (.+?) arguments, but only bound (.+?) \\(JoinPointMatch (.+?) bound in invocation\\)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"spring-aop/src/main/java/org/springframework/aop/aspectj/AbstractAspectJAdvice.java","lineNumber":603,"sourceCode":"\t\t\t}\n\t\t\t// binding from returning clause\n\t\t\tif (this.returningName != null) {\n\t\t\t\tInteger index = this.argumentBindings.get(this.returningName);\n\t\t\t\tAssert.state(index != null, \"Index must not be null\");\n\t\t\t\tadviceInvocationArgs[index] = returnValue;\n\t\t\t\tnumBound++;\n\t\t\t}\n\t\t\t// binding from thrown exception\n\t\t\tif (this.throwingName != null) {\n\t\t\t\tInteger index = this.argumentBindings.get(this.throwingName);\n\t\t\t\tAssert.state(index != null, \"Index must not be null\");\n\t\t\t\tadviceInvocationArgs[index] = ex;\n\t\t\t\tnumBound++;\n\t\t\t}\n\t\t}\n\n\t\tif (numBound != this.parameterTypes.length) {\n\t\t\tthrow new IllegalStateException(\"Required to bind \" + this.parameterTypes.length +\n\t\t\t\t\t\" arguments, but only bound \" + numBound + \" (JoinPointMatch \" +\n\t\t\t\t\t(jpMatch == null ? \"was NOT\" : \"WAS\") + \" bound in invocation)\");\n\t\t}\n\n\t\treturn adviceInvocationArgs;\n\t}\n\n\n\t/**\n\t * Invoke the advice method.\n\t * @param jpMatch the JoinPointMatch that matched this execution join point\n\t * @param returnValue the return value from the method execution (may be null)\n\t * @param ex the exception thrown by the method execution (may be null)\n\t * @return the invocation result\n\t * @throws Throwable in case of invocation failure\n\t */\n\tprotected @Nullable Object invokeAdviceMethod(@Nullable JoinPointMatch jpMatch,\n\t\t\t@Nullable Object returnValue, @Nullable Throwable ex) throws Throwable {","sourceCodeStart":585,"sourceCodeEnd":621,"githubUrl":"https://github.com/spring-projects/spring-framework/blob/e8729d043887bf0d0baf91e062e909b56eb2b708/spring-aop/src/main/java/org/springframework/aop/aspectj/AbstractAspectJAdvice.java#L585-L621","documentation":"Thrown by argBinding (line 602-606) at advice invocation time when the number of bound arguments (numBound) does not equal the advice method's parameter count. This is a runtime mismatch: the join-point match (jpMatch) did not supply all expected pointcut bindings, or returning/throwing bindings could not be populated.","triggerScenarios":"A pointcut that matched at proxy-creation time but at runtime yields a JoinPointMatch with fewer PointcutParameter bindings than the advice expects (e.g. the pointcut binds variables the advice signature requires, but the runtime match returned null or partial bindings); jpMatch being null when the advice has pointcut-bound parameters.","commonSituations":"Custom pointcut that inconsistently populates parameter bindings; concurrent/deserialization edge cases losing the JoinPointMatch user attribute; advice parameter list changed after proxy creation; a pointcut expression referencing variables not actually bound at every matched join point.","solutions":["Verify the pointcut expression binds exactly the variables the advice method declares (other than join-point/returning/throwing).","Ensure the JoinPointMatch is stored and retrieved for this advice's expression (ExposeInvocationInterceptor present, same expression key).","Rebuild proxies after changing the advice signature so bindings are recalculated.","Simplify the pointcut to deterministically bind all required parameters at every matched join point."],"exampleFix":"// before: pointcut binds 'id' but advice also declares 'name' with no binding source\n@After(value = \"execution(* svc.*(..)) && args(id,name)\")\npublic void after(Long id, String name) { ... } // matches a method with only one arg\n// after\n@After(value = \"execution(* svc.update(..)) && args(id,name)\")\npublic void after(Long id, String name) { ... } // matches a 2-arg method","handlingStrategy":"validation","validationCode":"// At setup, verify pointcut-bound parameter names are all produced by the pointcut.\nString[] adviceParamNames = new DefaultParameterNameDiscoverer().getParameterNames(adviceMethod);\nSet<String> boundByPointcut = pointcutExpressionParameterNames(pointcut.getExpression()); // your extractor\nfor (String n : requiredBindings(adviceParamNames, returningName, throwingName)) {\n    if (!boundByPointcut.contains(n)) {\n        throw new IllegalStateException(\"Pointcut does not bind parameter '\" + n + \"' required by \" + adviceMethod);\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    return advice.invokeAdviceMethod(jpMatch, returnValue, ex);\n} catch (IllegalStateException ex) {\n    if (ex.getMessage().startsWith(\"Required to bind\")) {\n        // log jpMatch null/partial and rethrow with context\n        throw new IllegalStateException(\"JoinPointMatch did not supply all bindings for \" + adviceMethod, ex);\n    }\n    throw ex;\n}","preventionTips":["Ensure the pointcut binds every variable the advice declares (besides join-point/returning/throwing).","Keep ExposeInvocationInterceptor in the chain so the JoinPointMatch is available at runtime.","Rebuild proxies after changing advice signatures or pointcuts.","Avoid pointcuts whose parameter bindings vary across matched join points."],"tags":["spring-aop","aspectj","argument-binding","pointcut-mismatch","runtime"],"analyzedSha":"e8729d043887bf0d0baf91e062e909b56eb2b708","analyzedAt":"2026-08-04T19:07:39.725Z","schemaVersion":2}