spring-projects/spring-framework · error · NotWritablePropertyException
Invalid property '{propertyName}' of bean class [{beanClass.
Error message
Invalid property '{propertyName}' of bean class [{beanClass.getName()}]: {matches.buildErrorMessage()} What it means
Thrown as NotWritablePropertyException by BeanWrapperImpl.createNotWritablePropertyException when a property name cannot be resolved to a writable property (no matching PropertyDescriptor with a write method). The message includes PropertyMatches.buildErrorMessage() which suggests similar property names, so the exception text usually shows 'Did you mean?' candidates.
Source
Thrown at spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java:205
TypeDescriptor td = ((GenericTypeAwarePropertyDescriptor) pd).getTypeDescriptor();
return convertForProperty(propertyName, null, value, td);
}
@Override
protected @Nullable PropertyHandler getLocalPropertyHandler(String propertyName) {
PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(propertyName);
return (pd != null ? new BeanPropertyHandler((GenericTypeAwarePropertyDescriptor) pd) : null);
}
@Override
protected BeanWrapperImpl newNestedPropertyAccessor(Object object, String nestedPath) {
return new BeanWrapperImpl(object, nestedPath, this);
}
@Override
protected NotWritablePropertyException createNotWritablePropertyException(String propertyName) {
PropertyMatches matches = PropertyMatches.forProperty(propertyName, getRootClass());
throw new NotWritablePropertyException(getRootClass(), getNestedPath() + propertyName,
matches.buildErrorMessage(), matches.getPossibleMatches());
}
@Override
public PropertyDescriptor[] getPropertyDescriptors() {
return getCachedIntrospectionResults().getPropertyDescriptors();
}
@Override
public PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException {
BeanWrapperImpl nestedBw = (BeanWrapperImpl) getPropertyAccessorForPropertyPath(propertyName);
String finalPath = getFinalPath(nestedBw, propertyName);
PropertyDescriptor pd = nestedBw.getCachedIntrospectionResults().getPropertyDescriptor(finalPath);
if (pd == null) {
throw new InvalidPropertyException(getRootClass(), getNestedPath() + propertyName,
"No property '" + propertyName + "' found");
}
return pd;View on GitHub (pinned to 69bf83ad71)
Solutions
- Use the suggested 'possible matches' from the exception message to correct the property name.
- Add a public setter for the property on the target class (or open it for field access if using a field-accessor).
- Allow unknown fields if expected: configure the DataBinder with setIgnoreUnknownFields(true) (Spring Boot @ConfigurationProperties) or skip the binding path.
Example fix
// before
wrapper.setPropertyValue("fristName", "Jane"); // typo
// after
wrapper.setPropertyValue("firstName", "Jane"); Defensive patterns
Strategy: validation
Validate before calling
// before binding
Set<String> writable = Arrays.stream(wrapper.getPropertyDescriptors())
.filter(pd -> pd.getWriteMethod() != null)
.map(PropertyDescriptor::getName).collect(Collectors.toSet());
if (!writable.contains(name)) {
throw new IllegalArgumentException("not writable: " + name);
} Type guard
static boolean isWritable(BeanWrapper w, String name) {
try { return w.getPropertyDescriptor(name).getWriteMethod() != null; }
catch (InvalidPropertyException | NotWritablePropertyException e) { return false; }
} Prevention
- Configure DataBinder with allowedFields and use ignoreUnknownFields where appropriate.
- Read the 'possible matches' in the exception - it usually contains the right name.
- Keep a single source of truth for property names (constants).
When it happens
Trigger: Calling setPropertyValue / setPropertyValues on a BeanWrapperImpl with a property name that does not correspond to a writable bean property of the root class. Triggered from the base AbstractNestablePropertyAccessor when getLocalPropertyHandler returns null and a write is attempted.
Common situations: Typo in a property name in @Value SpEL, DataBinder/ServletRequestDataBinder binding a request parameter that has no setter, @ConfigurationProperties prefix mismatch, or a setter that was removed/renamed.
Related errors
- Nested property in path '{propertyName}' does not exist
- No property handler found
- Invalid array index in property path '{tokens.canonicalName}
- Cannot set element with index {index} in List of size {size}
- Invalid list index in property path '{tokens.canonicalName}'
AI-assisted analysis of spring-projects/spring-framework@69bf83ad71 (2026-08-09).
Data as JSON: /api/errors/6c41b1f343014f49.
Report an issue: GitHub.