spring-projects/spring-framework · error · InvalidPropertyException

Invalid array index in property path

Error message

Invalid array index in property path '{tokens.canonicalName}'

What it means

Thrown by processKeyedProperty in the array branch when Array.set throws IndexOutOfBoundsException. Spring will auto-grow the array up to getAutoGrowCollectionLimit() (default 256); beyond that, or when autoGrow is disabled, the out-of-bounds is wrapped as InvalidPropertyException.

Solutions

  1. Pre-size / pre-allocate the target array so the index is in range.
  2. Enable autoGrowNestedPaths and raise setAutoGrowCollectionLimit if the index is legitimate (mind memory).
  3. Validate the incoming index on the controller/binder side and reject values beyond the array bounds.
  4. Switch from an array to a List/Map if indices are sparse or arbitrary.

Example fix

// before
wrapper.setAutoGrowNestedPaths(false);
wrapper.setPropertyValue("arr[5]", v); // arr.length == 0

// after
wrapper.setAutoGrowNestedPaths(true);
wrapper.setAutoGrowCollectionLimit(64);
wrapper.setPropertyValue("arr[5]", v);
Defensive patterns

Strategy: validation

Validate before calling

BeanWrapper w = new BeanWrapperImpl(bean);
String base = "arr"; int idx = requestedIndex;
Object arr = w.getPropertyValue(base);
int len = arr == null ? 0 : java.lang.reflect.Array.getLength(arr);
if (idx < 0 || idx >= len) {
    w.setAutoGrowNestedPaths(true);
    w.setAutoGrowCollectionLimit(Math.max(w.getAutoGrowCollectionLimit(), idx + 1));
}
w.setPropertyValue(base + "[" + idx + "]", value);

Type guard

static boolean inArrayBounds(Object array, int idx) {
    return array != null && array.getClass().isArray() && idx >= 0 && idx < java.lang.reflect.Array.getLength(array);
}

Try / catch

try {
    wrapper.setPropertyValue("arr[" + idx + "]", v);
} catch (InvalidPropertyException ex) {
    if (ex.getCause() instanceof IndexOutOfBoundsException) { /* pre-size array or raise 400 */ }
    else throw ex;
}

Prevention

When it happens

Trigger: setProperty("arr[3000]", value) with default autoGrowCollectionLimit=256; or autoGrowNestedPaths=false and arr.length <= index.

Common situations: Form binding array-indexed fields with sparse/large indices; autoGrow disabled explicitly; autoGrowCollectionLimit lowered; client sending an out-of-range index.

Related errors


AI-assisted analysis of spring-projects/spring-framework@69bf83ad71 (2026-08-09). Data as JSON: /api/errors/102f3f60d4975869. Report an issue: GitHub.

Appendix: source

Thrown at spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java:295

			try {
				if (isExtractOldValueForEditor() && arrayIndex < Array.getLength(propValue)) {
					oldValue = Array.get(propValue, arrayIndex);
				}
				Object convertedValue = convertIfNecessary(tokens.canonicalName, oldValue, pv.getValue(),
						componentType, ph.nested(tokens.keys.length));
				int length = Array.getLength(propValue);
				if (arrayIndex >= length && arrayIndex < getAutoGrowCollectionLimit()) {
					Object newArray = Array.newInstance(componentType, arrayIndex + 1);
					System.arraycopy(propValue, 0, newArray, 0, length);
					int lastKeyIndex = tokens.canonicalName.lastIndexOf('[');
					String propName = tokens.canonicalName.substring(0, lastKeyIndex);
					setPropertyValue(propName, newArray);
					propValue = getPropertyValue(propName);
				}
				Array.set(propValue, arrayIndex, convertedValue);
			}
			catch (IndexOutOfBoundsException ex) {
				throw new InvalidPropertyException(getRootClass(), this.nestedPath + tokens.canonicalName,
						"Invalid array index in property path '" + tokens.canonicalName + "'", ex);
			}
		}

		else if (propValue instanceof List list) {
			TypeDescriptor requiredType = ph.getCollectionType(tokens.keys.length);
			int index = Integer.parseInt(lastKey);
			Object oldValue = null;
			if (isExtractOldValueForEditor() && index < list.size()) {
				oldValue = list.get(index);
			}
			Object convertedValue = convertIfNecessary(tokens.canonicalName, oldValue, pv.getValue(),
					requiredType.getResolvableType().resolve(), requiredType);
			int size = list.size();
			if (index >= size && index < getAutoGrowCollectionLimit()) {
				for (int i = size; i < index; i++) {
					try {
						list.add(null);

View on GitHub (pinned to 69bf83ad71)