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

  1. Add a standard JavaBeans setter (setXxx) matching the property name and assignable value type.
  2. Write to an existing writable property instead of a derived one.
  3. 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

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


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/deca426f63810aa7. Report an issue: GitHub.