{"record":{"id":"df62642ca307ff6d","repo":"spring-projects/spring-framework","slug":"invalid-method-signature-signature-expected-c","errorCode":null,"errorMessage":"Invalid method signature '{signature}': expected closing ')' for args list","messagePattern":"Invalid method signature '(.+?)': expected closing '\\)' for args list","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"warning","filePath":"spring-beans/src/main/java/org/springframework/beans/BeanUtils.java","lineNumber":458,"sourceCode":"\t * argument type list, only the method whose name and argument types match will be returned.\n\t * <p>Note then that {@code methodName} and {@code methodName()} are <strong>not</strong>\n\t * resolved in the same way. The signature {@code methodName} means the method called\n\t * {@code methodName} with the least number of arguments, whereas {@code methodName()}\n\t * means the method called {@code methodName} with exactly 0 arguments.\n\t * <p>If no method can be found, then {@code null} is returned.\n\t * @param signature the method signature as String representation\n\t * @param clazz the class to resolve the method signature against\n\t * @return the resolved Method\n\t * @see #findMethod\n\t * @see #findMethodWithMinimalParameters\n\t */\n\tpublic static @Nullable Method resolveSignature(String signature, Class<?> clazz) {\n\t\tAssert.hasText(signature, \"'signature' must not be empty\");\n\t\tAssert.notNull(clazz, \"Class must not be null\");\n\t\tint startParen = signature.indexOf('(');\n\t\tint endParen = signature.indexOf(')');\n\t\tif (startParen > -1 && endParen == -1) {\n\t\t\tthrow new IllegalArgumentException(\"Invalid method signature '\" + signature +\n\t\t\t\t\t\"': expected closing ')' for args list\");\n\t\t}\n\t\telse if (startParen == -1 && endParen > -1) {\n\t\t\tthrow new IllegalArgumentException(\"Invalid method signature '\" + signature +\n\t\t\t\t\t\"': expected opening '(' for args list\");\n\t\t}\n\t\telse if (startParen == -1) {\n\t\t\treturn findMethodWithMinimalParameters(clazz, signature);\n\t\t}\n\t\telse {\n\t\t\tString methodName = signature.substring(0, startParen);\n\t\t\tString[] parameterTypeNames =\n\t\t\t\t\tStringUtils.commaDelimitedListToStringArray(signature.substring(startParen + 1, endParen));\n\t\t\tClass<?>[] parameterTypes = new Class<?>[parameterTypeNames.length];\n\t\t\tfor (int i = 0; i < parameterTypeNames.length; i++) {\n\t\t\t\tString parameterTypeName = parameterTypeNames[i].trim();\n\t\t\t\ttry {\n\t\t\t\t\tparameterTypes[i] = ClassUtils.forName(parameterTypeName, clazz.getClassLoader());","sourceCodeStart":440,"sourceCodeEnd":476,"githubUrl":"https://github.com/spring-projects/spring-framework/blob/69bf83ad716d0cfc4b0520a19b4d8b24c79d1538/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java#L440-L476","documentation":"Thrown as IllegalArgumentException by resolveSignature at BeanUtils.java:458 when the signature string contains an opening '(' but no matching closing ')'. The method expects 'name(arg,type,...)' form; a stray open paren without a close breaks parsing, so it refuses to guess and reports exactly which delimiter is missing.","triggerScenarios":"Calling BeanUtils.resolveSignature(\"doStuff(\", clazz) or \"doStuff(a, b\" — any signature where '(' appears but ')' does not. Common when concatenating/truncating a method signature string programmatically or in a typo'd XML/annotation value.","commonSituations":"Method-invocation / SpEL method lookups configured via strings; generated config that builds a signature and truncates it; copy-paste errors in @Bean/@Pointcut-like name expressions; YAML/properties whose value gets trimmed wrong.","solutions":["Correct the signature to include the closing parenthesis, e.g. \"doStuff()\".","Validate the signature has balanced parentheses before calling resolveSignature.","If you only want name-based lookup with minimal params, omit the parentheses entirely: \"doStuff\"."],"exampleFix":"// before\nMethod m = BeanUtils.resolveSignature(\"doStuff(\", clazz);\n\n// after\nMethod m = BeanUtils.resolveSignature(\"doStuff()\", clazz);","handlingStrategy":"validation","validationCode":"int open = signature.indexOf('('), close = signature.indexOf(')');\nif (open == -1 && close == -1) {\n    // name-only lookup is fine\n} else if (open > -1 && close > open) {\n    BeanUtils.resolveSignature(signature, clazz);\n} else {\n    throw new IllegalArgumentException(\"malformed signature: \" + signature);\n}","typeGuard":"static boolean wellFormedSignature(String s) {\n    int o = s.indexOf('('), c = s.indexOf(')');\n    return (o == -1 && c == -1) || (o > -1 && c > o);\n}","tryCatchPattern":"try {\n    BeanUtils.resolveSignature(sig, clazz);\n} catch (IllegalArgumentException ex) {\n    if (ex.getMessage().contains(\"closing ')'\")) {\n        sig = sig + \")\"; // or fix the source of the signature\n    } else throw ex;\n}","preventionTips":["Always pair '(' with ')' in method signature strings.","For name-only lookup, omit parentheses entirely.","Validate signature strings with a balanced-paren check before resolveSignature.","Avoid building signatures by string slicing without re-checking delimiters."],"tags":["spring-beans","beanutils","method-resolution","signature","parsing","validation"],"backgroundTag":null,"analyzedSha":"69bf83ad716d0cfc4b0520a19b4d8b24c79d1538","analyzedAt":"2026-08-09T15:32:58.770Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}