{"record":{"id":"0436f091d354c8fa","repo":"spring-projects/spring-framework","slug":"property-referenced-in-indexed-property-path-tok","errorCode":null,"errorMessage":"Property referenced in indexed property path '{tokens.canonicalName}' is neither an array nor a List nor a Map; returned value was [{propValue}]","messagePattern":"Property referenced in indexed property path '(.+?)' is neither an array nor a List nor a Map; returned value was \\[(.+?)\\]","errorType":"exception","errorClass":"InvalidPropertyException","httpStatus":null,"severity":"error","filePath":"spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java","lineNumber":354,"sourceCode":"\t\t\tTypeDescriptor mapKeyType = ph.getMapKeyType(tokens.keys.length);\n\t\t\tTypeDescriptor mapValueType = ph.getMapValueType(tokens.keys.length);\n\t\t\t// IMPORTANT: Do not pass full property name in here - property editors\n\t\t\t// must not kick in for map keys but rather only for map values.\n\t\t\tObject convertedMapKey = convertIfNecessary(null, null, lastKey,\n\t\t\t\t\tmapKeyType.getResolvableType().resolve(), mapKeyType);\n\t\t\tObject oldValue = null;\n\t\t\tif (isExtractOldValueForEditor()) {\n\t\t\t\toldValue = map.get(convertedMapKey);\n\t\t\t}\n\t\t\t// Pass full property name and old value in here, since we want full\n\t\t\t// conversion ability for map values.\n\t\t\tObject convertedMapValue = convertIfNecessary(tokens.canonicalName, oldValue, pv.getValue(),\n\t\t\t\t\tmapValueType.getResolvableType().resolve(), mapValueType);\n\t\t\tmap.put(convertedMapKey, convertedMapValue);\n\t\t}\n\n\t\telse {\n\t\t\tthrow new InvalidPropertyException(getRootClass(), this.nestedPath + tokens.canonicalName,\n\t\t\t\t\t\"Property referenced in indexed property path '\" + tokens.canonicalName +\n\t\t\t\t\t\"' is neither an array nor a List nor a Map; returned value was [\" + propValue + \"]\");\n\t\t}\n\t}\n\n\tprivate Object getPropertyHoldingValue(PropertyTokenHolder tokens) {\n\t\t// Apply indexes and map keys: fetch value for all keys but the last one.\n\t\tAssert.state(tokens.keys != null, \"No token keys\");\n\t\tPropertyTokenHolder getterTokens = new PropertyTokenHolder(tokens.actualName);\n\t\tgetterTokens.canonicalName = tokens.canonicalName;\n\t\tgetterTokens.keys = new String[tokens.keys.length - 1];\n\t\tSystem.arraycopy(tokens.keys, 0, getterTokens.keys, 0, tokens.keys.length - 1);\n\n\t\tObject propValue;\n\t\ttry {\n\t\t\tpropValue = getPropertyValue(getterTokens);\n\t\t}\n\t\tcatch (NotReadablePropertyException ex) {","sourceCodeStart":336,"sourceCodeEnd":372,"githubUrl":"https://github.com/spring-projects/spring-framework/blob/69bf83ad716d0cfc4b0520a19b4d8b24c79d1538/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java#L336-L372","documentation":"Thrown by processKeyedProperty's terminal else when a keyed/indexed write is attempted but the resolved property value is not an array, List, or Map. Since there is no container to address with a key/index, Spring rejects the write as InvalidPropertyException.","triggerScenarios":"setProperty(\"name[0]\", value) or setProperty(\"name[x]\", value) where 'name' resolves to a scalar (String, Integer, custom object) rather than a collection/array/map.","commonSituations":"Mismatched binding where the client treats a scalar field as indexed; the property was refactored from a collection to a scalar (or vice-versa) without updating the binder; binding 'field[0]' onto a String property expecting character semantics.","solutions":["Change the target property to an array/List/Map if indexed access is intended.","Fix the binding path to drop the index/key if the property is scalar.","Verify the property type via wrapper.getPropertyType(\"name\") before issuing the indexed write.","Update the form/binding source to match the actual bean shape."],"exampleFix":"// before\n// bean: private String name;  (scalar)\nwrapper.setPropertyValue(\"name[0]\", \"A\");\n\n// after\n// bean: private List<String> names = new ArrayList<>();\nwrapper.setPropertyValue(\"names[0]\", \"A\");","handlingStrategy":"type-guard","validationCode":"BeanWrapper w = new BeanWrapperImpl(bean);\nClass<?> type = w.getPropertyType(\"name\");\nif (type == null || !(type.isArray() || List.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type))) {\n    throw new IllegalArgumentException(\"'name' is not indexable: \" + type);\n}\nw.setPropertyValue(\"name[0]\", value);","typeGuard":"static boolean isIndexableType(Class<?> t) {\n    return t != null && (t.isArray() || List.class.isAssignableFrom(t) || Map.class.isAssignableFrom(t));\n}","tryCatchPattern":"try {\n    wrapper.setPropertyValue(path, value);\n} catch (InvalidPropertyException ex) {\n    if (ex.getMessage().contains(\"neither an array nor a List nor a Map\")) { /* drop the index or change type */ }\n    else throw ex;\n}","preventionTips":["Reserve indexed syntax for array/List/Map-typed properties only.","Validate property type before issuing indexed writes from dynamic paths.","Keep form field shapes consistent with the command object types."],"tags":["spring-beans","bean-wrapper","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"}