{"record":{"id":"1fb53de1fb32a46a","repo":"spring-projects/spring-framework","slug":"not-enough-arguments-in-method-to-satisfy-binding","errorCode":null,"errorMessage":"Not enough arguments in method to satisfy binding of returning and throwing variables","messagePattern":"Not enough arguments in method to satisfy binding of returning and throwing variables","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscoverer.java","lineNumber":234,"sourceCode":"\t * for this class for details on the algorithm used.\n\t * @param method the target {@link Method}\n\t * @return the parameter names\n\t */\n\t@Override\n\tpublic @Nullable String @Nullable [] getParameterNames(Method method) {\n\t\tthis.argumentTypes = method.getParameterTypes();\n\t\tthis.numberOfRemainingUnboundArguments = this.argumentTypes.length;\n\t\tthis.parameterNameBindings = new String[this.numberOfRemainingUnboundArguments];\n\n\t\tint minimumNumberUnboundArgs = 0;\n\t\tif (this.returningName != null) {\n\t\t\tminimumNumberUnboundArgs++;\n\t\t}\n\t\tif (this.throwingName != null) {\n\t\t\tminimumNumberUnboundArgs++;\n\t\t}\n\t\tif (this.numberOfRemainingUnboundArguments < minimumNumberUnboundArgs) {\n\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\"Not enough arguments in method to satisfy binding of returning and throwing variables\");\n\t\t}\n\n\t\ttry {\n\t\t\tint algorithmicStep = STEP_JOIN_POINT_BINDING;\n\t\t\twhile (this.numberOfRemainingUnboundArguments > 0 && algorithmicStep < STEP_FINISHED) {\n\t\t\t\tswitch (algorithmicStep++) {\n\t\t\t\t\tcase STEP_JOIN_POINT_BINDING -> {\n\t\t\t\t\t\tif (!maybeBindThisJoinPoint()) {\n\t\t\t\t\t\t\tmaybeBindThisJoinPointStaticPart();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase STEP_THROWING_BINDING -> maybeBindThrowingVariable();\n\t\t\t\t\tcase STEP_ANNOTATION_BINDING -> maybeBindAnnotationsFromPointcutExpression();\n\t\t\t\t\tcase STEP_RETURNING_BINDING -> maybeBindReturningVariable();\n\t\t\t\t\tcase STEP_PRIMITIVE_ARGS_BINDING -> maybeBindPrimitiveArgsFromPointcutExpression();\n\t\t\t\t\tcase STEP_THIS_TARGET_ARGS_BINDING -> maybeBindThisOrTargetOrArgsFromPointcutExpression();\n\t\t\t\t\tcase STEP_REFERENCE_PCUT_BINDING -> maybeBindReferencePointcutParameter();","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/spring-projects/spring-framework/blob/69bf83ad716d0cfc4b0520a19b4d8b24c79d1538/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscoverer.java#L216-L252","documentation":"Thrown by AspectJAdviceParameterNameDiscoverer.getParameterNames (line 233-236) when the advice method's parameter count is less than the minimum required to hold both the returning and throwing bindings. minimumNumberUnboundArgs counts 1 per non-null returningName and 1 per non-null throwingName; if the method has fewer parameters than that, it cannot possibly accept both bindings.","triggerScenarios":"Configuring an advice method with fewer parameters than the sum of returning + throwing bindings — e.g. a method taking a single Throwable parameter but both returning=\"r\" and throwing=\"ex\" are set. Since an advice can be afterReturning XOR afterThrowing, setting both is itself suspicious, but the discoverer fails when the count is insufficient.","commonSituations":"Accidentally setting both returning and throwing on the same advice, or an advice method whose signature was trimmed (params removed) without updating the returning/throwing configuration. Common in generic advice-registration code that copies both attributes.","solutions":["An advice method is either afterReturning or afterThrowing — set only the corresponding binding (returning XOR throwing), not both.","Ensure the method declares a parameter for each binding (one for returning and/or one for throwing).","Remove the extraneous returning/throwing attribute that does not apply to this advice type."],"exampleFix":"// before — both returning and throwing set, method has 1 param\n<aop:after-returning method=\"after\"\n    pointcut=\"execution(* *(..))\" returning=\"r\" throwing=\"ex\"/>\npublic void after(Object r) {} // needs 2 params but has 1\n\n// after — after-returning binds only returning\n<aop:after-returning method=\"after\"\n    pointcut=\"execution(* *(..))\" returning=\"r\"/>\npublic void after(Object r) {}","handlingStrategy":"validation","validationCode":"import java.lang.reflect.Method;\n\n// Before configuring, ensure the method has enough params for the bindings you intend.\nvoid assertEnoughParamsForBindings(Method adviceMethod, boolean returning, boolean throwing) {\n    int needed = (returning ? 1 : 0) + (throwing ? 1 : 0);\n    if (adviceMethod.getParameterCount() < needed) {\n        throw new IllegalStateException(\"method has \" + adviceMethod.getParameterCount()\n            + \" params but needs >= \" + needed + \" for returning/throwing\");\n    }\n}","typeGuard":"boolean hasEnoughParamsForBindings(Method m, boolean returning, boolean throwing) {\n    int needed = (returning ? 1 : 0) + (throwing ? 1 : 0);\n    return m.getParameterCount() >= needed;\n}","tryCatchPattern":"try {\n    advice.getPointcut();\n} catch (IllegalStateException ex) {\n    if (ex.getMessage().contains(\"returning and throwing variables\")) {\n        // set only returning XOR throwing, and add the matching parameter\n    } else { throw ex; }\n}","preventionTips":["Set either returning or throwing on an advice, never both.","Ensure the method declares a parameter for each binding.","Validate binding configuration against the method signature at startup."],"tags":["spring-aop","aspectj","argument-binding","parameter-discovery","configuration"],"backgroundTag":null,"analyzedSha":"69bf83ad716d0cfc4b0520a19b4d8b24c79d1538","analyzedAt":"2026-08-09T15:32:58.770Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}