{"id":"cd4f895361294696","repo":"spring-projects/spring-framework","slug":"write-method-must-have-exactly-1-or-2-parameters","errorCode":null,"errorMessage":"Write method must have exactly 1 or 2 parameters: ${method}","messagePattern":"Write method must have exactly 1 or 2 parameters: (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java","lineNumber":184,"sourceCode":"\t\t\t\texistingPd.setWriteMethod(method);\n\t\t\t}\n\t\t}\n\t\telse if (nParams == 2) {\n\t\t\tif (existingPd == null) {\n\t\t\t\tthis.propertyDescriptors.add(\n\t\t\t\t\t\tnew SimpleIndexedPropertyDescriptor(propertyName, null, null, null, method));\n\t\t\t}\n\t\t\telse if (existingPd instanceof IndexedPropertyDescriptor indexedPd) {\n\t\t\t\tindexedPd.setIndexedWriteMethod(method);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.propertyDescriptors.remove(existingPd);\n\t\t\t\tthis.propertyDescriptors.add(new SimpleIndexedPropertyDescriptor(\n\t\t\t\t\t\tpropertyName, existingPd.getReadMethod(), existingPd.getWriteMethod(), null, method));\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tthrow new IllegalArgumentException(\"Write method must have exactly 1 or 2 parameters: \" + method);\n\t\t}\n\t}\n\n\tprivate @Nullable PropertyDescriptor findExistingPropertyDescriptor(String propertyName, Class<?> propertyType) {\n\t\tfor (PropertyDescriptor pd : this.propertyDescriptors) {\n\t\t\tfinal Class<?> candidateType;\n\t\t\tfinal String candidateName = pd.getName();\n\t\t\tif (pd instanceof IndexedPropertyDescriptor indexedPd) {\n\t\t\t\tcandidateType = indexedPd.getIndexedPropertyType();\n\t\t\t\tif (candidateName.equals(propertyName) &&\n\t\t\t\t\t\t(candidateType.equals(propertyType) || candidateType.equals(propertyType.componentType()))) {\n\t\t\t\t\treturn pd;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tcandidateType = pd.getPropertyType();\n\t\t\t\tif (candidateName.equals(propertyName) &&\n\t\t\t\t\t\t(candidateType.equals(propertyType) || propertyType.equals(candidateType.componentType()))) {","sourceCodeStart":166,"sourceCodeEnd":202,"githubUrl":"https://github.com/spring-projects/spring-framework/blob/e8729d043887bf0d0baf91e062e909b56eb2b708/spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java#L166-L202","documentation":"IllegalArgumentException thrown by ExtendedBeanInfo when it encounters a method named like a setter (setX) whose parameter count is neither 1 (simple property) nor 2 (indexed property). ExtendedBeanInfo is the lenient BeanInfo implementation Spring uses when the class fails standard java.beans.Introspector rules; a setter with 0 or 3+ params violates even that leniency.","triggerScenarios":"Spring selecting ExtendedBeanInfo as the introspector (via ExtendedBeanInfoFactory) for a class whose setX method was given an unusual arity, e.g. setFoo() with no args, setFoo(a,b,c), or an overloaded setFoo(int, String, boolean).","commonSituations":"A method named setX for a reason other than property setting (e.g. configuration helper); overloaded setter that breaks JavaBean conventions; reflective code-generators producing method names beginning with 'set' that aren't actually setters.","solutions":["Rename the method so it does not look like a setter (does not start with 'set').","If it must keep the name, give it exactly 1 (simple) or 2 (int-indexed) parameters.","Force standard IntrospectionResults by avoiding the ExtendedBeanInfoFactory SPI registration if the class is incompatible.","Exclude the method from introspection by making it non-public if it is internal."],"exampleFix":"// before\npublic void setStatus(String code, String reason, long ts) { ... }  // 3-arg 'setter'\n\n// after\npublic void recordStatus(String code, String reason, long ts) { ... }","handlingStrategy":"validation","validationCode":"// Scan candidate setter methods before introspection\nfor (Method m : MyClass.class.getMethods()) {\n    if (m.getName().startsWith(\"set\") && m.getName().length() > 3) {\n        int n = m.getParameterCount();\n        if (n != 1 && n != 2) {\n            throw new IllegalStateException(\"Suspicious setter arity: \" + m);\n        }\n    }\n}","typeGuard":"static boolean hasOnlyValidSetterArities(Class<?> c) {\n    for (Method m : c.getMethods()) {\n        if (m.getName().startsWith(\"set\") && m.getName().length() > 3) {\n            int n = m.getParameterCount();\n            if (n != 1 && n != 2) return false;\n        }\n    }\n    return true;\n}","tryCatchPattern":"try {\n    return new ExtendedBeanInfo(clazz).getPropertyDescriptors();\n} catch (IllegalArgumentException ex) {\n    if (ex.getMessage().startsWith(\"Write method must have exactly 1 or 2 parameters\")) {\n        // log the offending method and fall back to standard Introspector\n        return java.beans.Introspector.getBeanInfo(clazz).getPropertyDescriptors();\n    }\n    throw ex;\n}","preventionTips":["Do not name non-setter methods with the 'set' prefix unless they take 1 or 2 args.","Use record() or fluent accessors (loadX/applyX) for command-like methods.","Add a static analysis rule flagging 'set'-prefixed methods with invalid arity."],"tags":["extended-bean-info","introspection","setter","spring-beans"],"analyzedSha":"e8729d043887bf0d0baf91e062e909b56eb2b708","analyzedAt":"2026-08-04T19:07:39.725Z","schemaVersion":2}