spring-projects/spring-framework · error · InvalidPropertyException
Property referenced in indexed property path
Error message
Property referenced in indexed property path '{tokens.canonicalName}' is neither an array nor a List nor a Map; returned value was [{propValue}] What it means
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.
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.
Example fix
// before
// bean: private String name; (scalar)
wrapper.setPropertyValue("name[0]", "A");
// after
// bean: private List<String> names = new ArrayList<>();
wrapper.setPropertyValue("names[0]", "A"); Defensive patterns
Strategy: type-guard
Validate before calling
BeanWrapper w = new BeanWrapperImpl(bean);
Class<?> type = w.getPropertyType("name");
if (type == null || !(type.isArray() || List.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type))) {
throw new IllegalArgumentException("'name' is not indexable: " + type);
}
w.setPropertyValue("name[0]", value); Type guard
static boolean isIndexableType(Class<?> t) {
return t != null && (t.isArray() || List.class.isAssignableFrom(t) || Map.class.isAssignableFrom(t));
} Try / catch
try {
wrapper.setPropertyValue(path, value);
} catch (InvalidPropertyException ex) {
if (ex.getMessage().contains("neither an array nor a List nor a Map")) { /* drop the index or change type */ }
else throw ex;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Property referenced in indexed property path
- Cannot access indexed value in property referenced in…
- Cannot access indexed value in property referenced in…
- Cannot access indexed value of property referenced in…
- Invalid property ' ' of bean class [ ]: Invalid index in…
AI-assisted analysis of spring-projects/spring-framework@69bf83ad71 (2026-08-09).
Data as JSON: /api/errors/0436f091d354c8fa.
Report an issue: GitHub.
Appendix: source
Thrown at spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java:354
TypeDescriptor mapKeyType = ph.getMapKeyType(tokens.keys.length);
TypeDescriptor mapValueType = ph.getMapValueType(tokens.keys.length);
// IMPORTANT: Do not pass full property name in here - property editors
// must not kick in for map keys but rather only for map values.
Object convertedMapKey = convertIfNecessary(null, null, lastKey,
mapKeyType.getResolvableType().resolve(), mapKeyType);
Object oldValue = null;
if (isExtractOldValueForEditor()) {
oldValue = map.get(convertedMapKey);
}
// Pass full property name and old value in here, since we want full
// conversion ability for map values.
Object convertedMapValue = convertIfNecessary(tokens.canonicalName, oldValue, pv.getValue(),
mapValueType.getResolvableType().resolve(), mapValueType);
map.put(convertedMapKey, convertedMapValue);
}
else {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + tokens.canonicalName,
"Property referenced in indexed property path '" + tokens.canonicalName +
"' is neither an array nor a List nor a Map; returned value was [" + propValue + "]");
}
}
private Object getPropertyHoldingValue(PropertyTokenHolder tokens) {
// Apply indexes and map keys: fetch value for all keys but the last one.
Assert.state(tokens.keys != null, "No token keys");
PropertyTokenHolder getterTokens = new PropertyTokenHolder(tokens.actualName);
getterTokens.canonicalName = tokens.canonicalName;
getterTokens.keys = new String[tokens.keys.length - 1];
System.arraycopy(tokens.keys, 0, getterTokens.keys, 0, tokens.keys.length - 1);
Object propValue;
try {
propValue = getPropertyValue(getterTokens);
}
catch (NotReadablePropertyException ex) {View on GitHub (pinned to 69bf83ad71)