spring-projects/spring-framework · error · NullValueInNestedPathException

Value of nested property '${nestedPath}${canonicalName}' is

Error message

Value of nested property '${nestedPath}${canonicalName}' is null

What it means

Thrown as NullValueInNestedPathException by AbstractNestablePropertyAccessor when resolving a nested property path (e.g. 'address.city') and an intermediate segment is null while auto-grow-nested-paths is disabled. Spring refuses to silently descend into null because it cannot obtain a BeanWrapper for the missing intermediate bean. The path string in the message is the accumulated nested path plus the canonical property name.

Source

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

	 * @param nestedProperty property to create the PropertyAccessor for
	 * @return the PropertyAccessor instance, either cached or newly created
	 */
	private AbstractNestablePropertyAccessor getNestedPropertyAccessor(String nestedProperty) {
		Map<String, AbstractNestablePropertyAccessor> nestedAccessors = this.nestedPropertyAccessors;
		if (nestedAccessors == null) {
			nestedAccessors = new HashMap<>();
			this.nestedPropertyAccessors = nestedAccessors;
		}
		// Get value of bean property.
		PropertyTokenHolder tokens = getPropertyNameTokens(nestedProperty);
		String canonicalName = tokens.canonicalName;
		Object value = getPropertyValue(tokens);
		if (value == null || (value instanceof Optional<?> optional && optional.isEmpty())) {
			if (isAutoGrowNestedPaths()) {
				value = setDefaultValue(tokens);
			}
			else {
				throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + canonicalName);
			}
		}

		// Lookup cached sub-PropertyAccessor, create new one if not found.
		AbstractNestablePropertyAccessor nestedPa = nestedAccessors.get(canonicalName);
		if (nestedPa == null || nestedPa.getWrappedInstance() != ObjectUtils.unwrapOptional(value)) {
			if (logger.isTraceEnabled()) {
				logger.trace("Creating new nested " + getClass().getSimpleName() + " for property '" + canonicalName + "'");
			}
			nestedPa = newNestedPropertyAccessor(value, this.nestedPath + canonicalName + NESTED_PROPERTY_SEPARATOR);
			// Inherit all type-specific PropertyEditors.
			copyDefaultEditorsTo(nestedPa);
			copyCustomEditorsTo(nestedPa, canonicalName);
			nestedAccessors.put(canonicalName, nestedPa);
		}
		else {
			if (logger.isTraceEnabled()) {
				logger.trace("Using cached nested property accessor for property '" + canonicalName + "'");

View on GitHub (pinned to e8729d0438)

Solutions

  1. Initialize the nested property in the target class (e.g. private Address address = new Address();) so the path is never null.
  2. Enable auto-grow: wrapper.setAutoGrowNestedPaths(true) or set 'spring.databinding.auto-grow-nested-paths=true' where supported, so Spring creates intermediate instances.
  3. Bind a non-null instance before setting nested values, or restructure the bean so the intermediate is set at construction.
  4. Catch NullValueInNestedPathException around binding and report a user-facing validation error instead of propagating.

Example fix

// before
Order order = new Order(); // order.address == null
BeanWrapper w = new BeanWrapperImpl(order);
w.setPropertyValue("address.city", "NYC"); // -> NullValueInNestedPathException

// after (initialize nested bean)
public class Order { private Address address = new Address(); }
// or: w.setAutoGrowNestedPaths(true);
Defensive patterns

Strategy: validation

Validate before calling

// Before binding a nested path, ensure intermediates are non-null
public static void ensurePath(Object root, String... props) throws Exception {
  Object cur = root;
  StringBuilder path = new StringBuilder();
  for (String p : props) {
    path.append(p).append('.');
    PropertyDescriptor pd = Arrays.stream(
        Introspector.getBeanInfo(cur.getClass()).getPropertyDescriptors())
        .filter(d -> d.getName().equals(p)).findFirst().orElseThrow();
    Object next = pd.getReadMethod().invoke(cur);
    if (next == null) {
      Object made = pd.getPropertyType().getDeclaredConstructor().newInstance();
      pd.getWriteMethod().invoke(cur, made);
      next = made;
    }
    cur = next;
  }
}

Type guard

public static boolean isNestedPathResolvable(BeanWrapper w, String path) {
  try { return w.getPropertyValue(path) != null || w.isAutoGrowNestedPaths(); }
  catch (NullValueInNestedPathException e) { return false; }
}

Try / catch

try {
  wrapper.setPropertyValue("address.city", value);
} catch (NullValueInNestedPathException e) {
  // surface as a binding/validation error: "missing intermediate for " + e.getPropertyName()
}

Prevention

When it happens

Trigger: Calling BeanWrapperImpl.setPropertyValue('a.b.c', val) / DataBinder binding when the target's 'a' (or 'a.b') field is null and wrapper.setAutoGrowNestedPaths(false) (the default). Also triggered by getNestedPropertyAccessor when reading a nested path whose value is null or an empty Optional.

Common situations: Binding a form-backing bean where the nested object was never initialized (e.g. new Order() with a null Address). YAML/properties binding into a graph that has null intermediate references. Spring MVC form binding to a command object whose nested entity field is null.

Related errors


AI-assisted analysis of spring-projects/spring-framework@e8729d0438 (2026-08-04). Data as JSON: /data/errors/d37d7d34d540c3e4.json. Report an issue: GitHub.