flowable/flowable-engine · error · PropertyNotFoundException
Property ' ' not found on type
Error message
Property '${propertyName}' not found on type '${base.getClass().getName()}' What it means
RecordELResolver.getValue resolves record component accessors via reflection: it looks up a public no-arg method named exactly like the property on the record class. If getMethod finds no such accessor it wraps the NoSuchMethodException in a PropertyNotFoundException.
Solutions
- Fix the property name in the expression to match the record component exactly (case-sensitive)
- Add or rename the record component/accessor to match the expression
- Check the record class version on the classpath matches what the expression expects
- Catch PropertyNotFoundException during EL evaluation and log the expression and type
Example fix
// before
public record Order(String OrderRef) {}
${order.orderRef} // not found: component is OrderRef
// after
public record Order(String orderRef) {}
${order.orderRef} Defensive patterns
Strategy: validation
Validate before calling
boolean hasProperty(Class<?> t, String name){ try { t.getMethod(name); return true; } catch (NoSuchMethodException e){ return false; } } Type guard
boolean isRecordAccessor(Class<?> t, String n){ return java.lang.reflect.RecordComponent.class.isAssignableFrom(Object.class) && hasProperty(t, n); } Try / catch
try { return expr.getValue(ctx); } catch (PropertyNotFoundException e) { log.error("Unknown EL property: {}", e.getMessage()); throw e; } Prevention
- Match property names to record components exactly
- Update expressions when renaming components
- Evaluate all referenced expressions in unit tests
- Watch case sensitivity (name vs Name)
When it happens
Trigger: Evaluating ${record.property} where the record has no component (or accessor method) with that exact name; typos in property names; using a method name that exists but with parameters or is inherited/non-accessor.
Common situations: Renaming a record component without updating EL expressions in process/BPMN XML; expressions written for POJO getters (e.g. getName) applied to records that expose name(); case mismatch (Name vs name).
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/6a549dc54d6cec97.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/javax/el/RecordELResolver.java:70
* @throws PropertyNotFoundException if the {@code base} is an instance of {@link Record} and the specified property
* does not exist.
* @throws ELException if an exception was throws while performing the property resolution. The thrown
* exception must be included as the cause of this exception, if available.
*/
@Override
public Object getValue(ELContext context, Object base, Object property) {
Objects.requireNonNull(context);
if (base instanceof Record && property != null) {
context.setPropertyResolved(base, property);
String propertyName = property.toString();
Method method;
try {
method = base.getClass().getMethod(propertyName);
} catch (NoSuchMethodException nsme) {
throw new PropertyNotFoundException("Property '" + propertyName + "' not found on type '" + base.getClass().getName() + "'", nsme);
}
if (Modifier.isPublic(method.getModifiers())) {
method.trySetAccessible();
}
try {
return method.invoke(base);
} catch (ReflectiveOperationException e) {
throw new ELException("Error reading '" + propertyName + "' on type '" + base.getClass().getName() + "'", e);
}
}
return null;
}
/**
* If the base object is an instance of {@link Record}, always returns {@code null} since {@link Record}s are always
* read-only.
* <p>View on GitHub (pinned to d6d39ce1c6)