flowable/flowable-engine · error · PropertyNotWritableException
Cannot write property: '${property}' on type ${base.getClass
Error message
Cannot write property: '${property}' on type ${base.getClass().getName()} What it means
BeanELResolver.setValue throws PropertyNotWritableException when the target bean property has no writable setter — BeanProperty.write(base) returns null, meaning no public setter method exists for the property being assigned.
Source
Thrown at modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/javax/el/BeanELResolver.java:182
@Override
public void setValue(ELContext context, Object base, Object property, Object value) {
Objects.requireNonNull(context, "context is null");
if (base == null || property == null) {
return;
}
if (readOnly) {
throw new PropertyNotWritableException("resolver is read-only");
}
BeanProperty beanProperty = property(base, property);
if (beanProperty == null) {
return;
}
Method method = beanProperty.write(base);
if (method == null) {
throw new PropertyNotWritableException("Cannot write property: '" + property + "' on type " + base.getClass().getName());
}
context.setPropertyResolved(base, property);
try {
invoke(method, base, value);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
Util.handleThrowable(cause);
throw new ELException("Error '" + property + "' on type '" + base.getClass().getName() + "'", cause);
} catch (Exception e) {
throw new ELException(e);
}
}
@Override
public Object invoke(ELContext context, Object base, Object method, Class<?>[] paramTypes, Object[] params) {
Objects.requireNonNull(context, "context is null");
if (base == null || method == null) {
return null;View on GitHub (pinned to d6d39ce1c6)
Solutions
- Add a standard JavaBeans setter (setXxx) matching the property name and assignable value type.
- Write to an existing writable property instead of a derived one.
- Perform the mutation directly in Java code rather than via EL assignment.
Example fix
// before
public class User { public String getName() {...} } // no setter
// after
public class User { public String getName() {...} public void setName(String n) {...} } Defensive patterns
Strategy: validation
Validate before calling
if (new PropertyDescriptor(prop, bean.getClass()).getWriteMethod() == null) throw new IllegalStateException("no setter for " + prop); Type guard
boolean writableViaEl(Object bean, String prop) { try { return new PropertyDescriptor(prop, bean.getClass()).getWriteMethod() != null; } catch (IntrospectionException e) { return false; } } Try / catch
try { resolver.setValue(ctx, base, prop, val); } catch (PropertyNotWritableException e) { /* add setter or write in code */ } Prevention
- Follow JavaBeans naming exactly (setXxx matching getXxx)
- Only assign to properties with public setters
- Introspect target beans in tests before enabling EL writes
When it happens
Trigger: Evaluating ${bean.field = value} where the bean exposes a getter but no setter (immutable/read-only property), or the setter doesn't match JavaBeans naming conventions.
Common situations: Assigning to computed/derived properties, final fields with only getters, or beans whose setters are named unconventionally (e.g. setHostName vs property 'hostname' casing mismatches).
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Error '${property}' on type '${base.getClass().getName()}'
- Error reading '' on type ''
- ELException
- resolver is read-only
- Cannot find method ${name} with ${params.length} parameters
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/deca426f63810aa7.
Report an issue: GitHub.