{"record":{"id":"f59a4db5f7231f69","repo":"spring-projects/spring-framework","slug":"throwing-name-is-neither-a-valid-argument-nam","errorCode":null,"errorMessage":"Throwing name '{}' is neither a valid argument name nor the fully-qualified name of a Java type on the classpath. Root cause: {}","messagePattern":"Throwing name '(.+?)' is neither a valid argument name nor the fully-qualified name of a Java type on the classpath\\. Root cause: (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"spring-aop/src/main/java/org/springframework/aop/aspectj/AbstractAspectJAdvice.java","lineNumber":344,"sourceCode":"\t\tthrow new UnsupportedOperationException(\"Only afterThrowing advice can be used to bind a thrown exception\");\n\t}\n\n\t/**\n\t * We need to hold the throwing name at this level for argument binding calculations,\n\t * this method allows the afterThrowing advice subclass to set the name.\n\t */\n\tprotected void setThrowingNameNoCheck(String name) {\n\t\t// name could be a variable or a type...\n\t\tif (isVariableName(name)) {\n\t\t\tthis.throwingName = name;\n\t\t}\n\t\telse {\n\t\t\t// assume a type\n\t\t\ttry {\n\t\t\t\tthis.discoveredThrowingType = ClassUtils.forName(name, getAspectClassLoader());\n\t\t\t}\n\t\t\tcatch (Throwable ex) {\n\t\t\t\tthrow new IllegalArgumentException(\"Throwing name '\" + name +\n\t\t\t\t\t\t\"' is neither a valid argument name nor the fully-qualified \" +\n\t\t\t\t\t\t\"name of a Java type on the classpath. Root cause: \" + ex);\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected Class<?> getDiscoveredThrowingType() {\n\t\treturn this.discoveredThrowingType;\n\t}\n\n\t@Contract(\"null -> false\")\n\tprivate static boolean isVariableName(@Nullable String name) {\n\t\treturn AspectJProxyUtils.isVariableName(name);\n\t}\n\n\n\t/**\n\t * Do as much work as we can as part of the set-up so that argument binding","sourceCodeStart":326,"sourceCodeEnd":362,"githubUrl":"https://github.com/spring-projects/spring-framework/blob/69bf83ad716d0cfc4b0520a19b4d8b24c79d1538/spring-aop/src/main/java/org/springframework/aop/aspectj/AbstractAspectJAdvice.java#L326-L362","documentation":"Thrown by setThrowingNameNoCheck (line 333-348) when the throwing value is neither a Java variable name (fails isVariableName) nor resolvable as a fully-qualified type via ClassUtils.forName on the aspect class loader. In type form it restricts which exception types trigger the advice; in variable form it binds the thrown exception. Failure of both means binding cannot proceed.","triggerScenarios":"Setting throwing=\"java.io.NonexistentException\" where the type is not on the classpath, or throwing=\"err-code\" (neither identifier nor type). Occurs via XML 'throwing' attribute or @AfterThrowing(throwing=...) when the value is wrong.","commonSituations":"Misspelled exception class name, a custom exception class in a jar not loaded by the aspect class loader, or using a non-identifier string as the throwing name. Common after moving exception classes between packages.","solutions":["If you meant a variable name, use a valid Java identifier matching an advice parameter of a Throwable subtype (e.g. throwing=\"ex\").","If you meant a type restriction, use the exact fully-qualified exception name and confirm it is visible to getAspectClassLoader().","Read the root cause (usually ClassNotFoundException) in the message to locate the missing type.","Rebuild/redeploy so the exception class is on the runtime classpath."],"exampleFix":"// before — misspelled exception type\n@AfterThrowing(pointcut=\"execution(* svc.*(..))\", throwing=\"java.lang.RuntimeErr\")\npublic void onError(Throwable t) {}\n\n// after — valid variable name bound to the advice parameter\n@AfterThrowing(pointcut=\"execution(* svc.*(..))\", throwing=\"t\")\npublic void onError(Throwable t) {}","handlingStrategy":"validation","validationCode":"import org.springframework.util.ClassUtils;\n\nvoid safeSetThrowingName(AbstractAspectJAdvice advice, String name) {\n    if (isJavaIdentifier(name) || isLoadableType(name, advice.getAspectClassLoader())) {\n        advice.setThrowingName(name); // routed to the afterThrowing subclass\n    } else {\n        throw new IllegalArgumentException(\"throwing '\" + name + \"' is neither identifier nor loadable type\");\n    }\n}\nboolean isLoadableType(String name, ClassLoader cl) {\n    try { ClassUtils.forName(name, cl); return true; } catch (Throwable t) { return false; }\n}","typeGuard":"boolean isThrowingNameValid(String name, ClassLoader cl) {\n    if (name == null) return false;\n    if (Character.isJavaIdentifierStart(name.charAt(0))) {\n        return name.chars().skip(1).allMatch(Character::isJavaIdentifierPart);\n    }\n    try { Class.forName(name, false, cl); return true; } catch (Throwable t) { return false; }\n}","tryCatchPattern":"try {\n    ((AspectJAfterThrowingAdvice) advice).setThrowingName(name);\n} catch (IllegalArgumentException ex) {\n    // root cause is usually ClassNotFoundException; correct the name/classpath\n}","preventionTips":["Prefer a variable name bound to a Throwable parameter over a type restriction.","Ensure the exception class jar is on the aspect class loader.","Update the throwing attribute when exception packages change."],"tags":["spring-aop","aspectj","after-throwing","classpath","argument-binding"],"backgroundTag":null,"analyzedSha":"69bf83ad716d0cfc4b0520a19b4d8b24c79d1538","analyzedAt":"2026-08-09T15:32:58.770Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}