{"record":{"id":"ec13175ee8889aa6","repo":"spring-projects/spring-framework","slug":"type-mismatch-between-indexed-and-non-indexed-meth","errorCode":null,"errorMessage":"Type mismatch between indexed and non-indexed methods: {} - {}","messagePattern":"Type mismatch between indexed and non-indexed methods: (.+?) - (.+?)","errorType":"exception","errorClass":"IntrospectionException","httpStatus":null,"severity":"error","filePath":"spring-beans/src/main/java/org/springframework/beans/PropertyDescriptorUtils.java","lineNumber":239,"sourceCode":"\t\t\t\t\t// Write method's property type potentially more specific\n\t\t\t\t\tindexedPropertyType = params[1];\n\t\t\t\t}\n\t\t\t\telse if (params[1].isAssignableFrom(indexedPropertyType)) {\n\t\t\t\t\t// Proceed with read method's property type\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthrow new IntrospectionException(\"Type mismatch between indexed read and write methods: \" +\n\t\t\t\t\t\t\tindexedReadMethod + \" - \" + indexedWriteMethod);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tindexedPropertyType = params[1];\n\t\t\t}\n\t\t}\n\n\t\tif (propertyType != null && (!propertyType.isArray() ||\n\t\t\t\tpropertyType.componentType() != indexedPropertyType)) {\n\t\t\tthrow new IntrospectionException(\"Type mismatch between indexed and non-indexed methods: \" +\n\t\t\t\t\tindexedReadMethod + \" - \" + indexedWriteMethod);\n\t\t}\n\n\t\treturn indexedPropertyType;\n\t}\n\n\t/**\n\t * Compare the given {@code PropertyDescriptors} and return {@code true} if\n\t * they are equivalent, i.e. their read method, write method, property type,\n\t * property editor and flags are equivalent.\n\t * @see java.beans.PropertyDescriptor#equals(Object)\n\t */\n\tpublic static boolean equals(PropertyDescriptor pd, PropertyDescriptor otherPd) {\n\t\treturn (ObjectUtils.nullSafeEquals(pd.getReadMethod(), otherPd.getReadMethod()) &&\n\t\t\t\tObjectUtils.nullSafeEquals(pd.getWriteMethod(), otherPd.getWriteMethod()) &&\n\t\t\t\tObjectUtils.nullSafeEquals(pd.getPropertyType(), otherPd.getPropertyType()) &&\n\t\t\t\tObjectUtils.nullSafeEquals(pd.getPropertyEditorClass(), otherPd.getPropertyEditorClass()) &&\n\t\t\t\tpd.isBound() == otherPd.isBound() && pd.isConstrained() == otherPd.isConstrained());","sourceCodeStart":221,"sourceCodeEnd":257,"githubUrl":"https://github.com/spring-projects/spring-framework/blob/69bf83ad716d0cfc4b0520a19b4d8b24c79d1538/spring-beans/src/main/java/org/springframework/beans/PropertyDescriptorUtils.java#L221-L257","documentation":"Thrown when a property exposes both a non-indexed accessor (getX()/setX()) and indexed accessors (getX(int)/setX(int, T)), but the non-indexed property type is not an array whose component type equals the indexed element type. Raised at line 239: propertyType must be an array and propertyType.componentType() must equal indexedPropertyType, otherwise IntrospectionException.","triggerScenarios":"A bean has List<String> getItems() as the non-indexed getter plus String getItem(int) — List is not an array, so the isArray() check fails. Or String[] getItems() with Integer getItem(int) — component type mismatch.","commonSituations":"Wrapping an internal List but also providing positional indexed accessors; refactoring from arrays to List collections while leaving the indexed methods in place; code-generated beans mixing collection and array styles.","solutions":["Align the non-indexed accessor to return an array whose component type matches the indexed element type (e.g. String[] getItems() with String getItem(int)).","Or remove the non-indexed getter/setter so the property is treated as indexed-only.","Or remove the indexed accessors if the collection-style getter is the intended public API."],"exampleFix":"// before\nprivate List<String> items;\npublic List<String> getItems() { return items; }\npublic String getItem(int i) { return items.get(i); }\n\n// after\nprivate String[] items;\npublic String[] getItems() { return items; }\npublic String getItem(int i) { return items[i]; }","handlingStrategy":"validation","validationCode":"// Ensure non-indexed type is array with matching component when indexed accessors exist\nClass<?> pt = nonIndexedRead.getReturnType();\nClass<?> it = indexedRead.getReturnType();\nif (pt != null && (!pt.isArray() || pt.componentType() != it)) {\n    throw new IllegalStateException(\"non-indexed type must be \" + it + \"[]\");\n}","typeGuard":"static boolean indexedMatchesNonIndexed(Class<?> nonIndexedType, Class<?> indexedType) {\n    return nonIndexedType == null || (nonIndexedType.isArray() && nonIndexedType.componentType() == indexedType);\n}","tryCatchPattern":null,"preventionTips":["When a bean has both getX() and getX(int), make getX() return T[].","If you switched to List<T>, drop the indexed getX(int)/setX(int, T) methods.","Test introspection in a unit test so mismatches surface before Spring binds."],"tags":["java-beans","introspection","indexed-property","type-mismatch"],"backgroundTag":null,"analyzedSha":"69bf83ad716d0cfc4b0520a19b4d8b24c79d1538","analyzedAt":"2026-08-09T15:32:58.770Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}