flowable/flowable-engine · error · FlowableException
Error while invoking '<fieldName>' on class <className>
Error message
Error while invoking '<fieldName>' on class <className>
What it means
ClassDelegateUtil.applyFieldDeclaration injects <field> values declared in BPMN into the delegate instance via a setter found with ReflectUtil. When the setter exists but invoking it throws IllegalArgumentException (argument type not acceptable to the method), a FlowableException wrapping it is thrown with the field name and class. This typically means the field value type in the XML does not match the setter's parameter type.
Source
Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/ClassDelegateUtil.java:55
return object;
}
public static void applyFieldDeclaration(List<FieldDeclaration> fieldDeclarations, Object target) {
if (fieldDeclarations != null) {
for (FieldDeclaration declaration : fieldDeclarations) {
applyFieldDeclaration(declaration, target);
}
}
}
public static void applyFieldDeclaration(FieldDeclaration declaration, Object target) {
Method setterMethod = ReflectUtil.getSetter(declaration.getName(), target.getClass(), declaration.getValue().getClass());
if (setterMethod != null) {
try {
setterMethod.invoke(target, declaration.getValue());
} catch (IllegalArgumentException e) {
throw new FlowableException("Error while invoking '" + declaration.getName() + "' on class " + target.getClass().getName(), e);
} catch (IllegalAccessException e) {
throw new FlowableException("Illegal access when calling '" + declaration.getName() + "' on class " + target.getClass().getName(), e);
} catch (InvocationTargetException e) {
throw new FlowableException("Exception while invoking '" + declaration.getName() + "' on class " + target.getClass().getName(), e);
}
} else {
Field field = ReflectUtil.getField(declaration.getName(), target);
if (field == null) {
throw new FlowableIllegalArgumentException("Field definition uses non-existing field '" + declaration.getName() + "' on class " + target.getClass().getName());
}
// Check if the delegate field's type is correct
if (!fieldTypeCompatible(declaration, field)) {
throw new FlowableIllegalArgumentException("Incompatible type set on field declaration '" + declaration.getName() + "' for class " + target.getClass().getName() + ". Declared value has type "
+ declaration.getValue().getClass().getName() + ", while expecting " + field.getType().getName());
}
ReflectUtil.setField(field, target, declaration.getValue());
}
}View on GitHub (pinned to d6d39ce1c6)
Solutions
- Align the setter's parameter type with what the BPMN field declaration produces (accept Expression for flowable:expression/string fields).
- Declare the field in BPMN with the matching type element (flowable:string, flowable:expression) for the setter.
- Change the field name so ReflectUtil resolves a setter whose parameter matches the value type.
- Inspect the cause chain (the wrapped IllegalArgumentException) to see the exact type mismatch.
Example fix
// before
public void setRetries(int retries) { ... }
<!-- <flowable:field name="retries"><flowable:string value="3"/> </flowable:field> -->
// after
public void setRetries(Expression retries) { ... }
<!-- or declare a numeric field type; then retries.getValueAsInt(execution) --> Defensive patterns
Strategy: try-catch
Validate before calling
Method m = ReflectUtil.getSetter(field.getName(), delegateClass, value.getClass());
if (m == null) throw new IllegalStateException("No matching setter for field " + field.getName()); Type guard
boolean hasMatchingSetter(Class<?> target, String field, Object value) {
return ReflectUtil.getSetter(field, target, value.getClass()) != null;
} Try / catch
try {
runtimeService.startProcessInstanceByKey("proc");
} catch (FlowableException e) {
if (e.getMessage().startsWith("Error while invoking '")) { /* fix field/setter types */ }
throw e;
} Prevention
- Declare fields in BPMN with the type element matching the setter parameter.
- Accept Expression for string/expression fields and resolve at execution time.
- Match field names to setter names exactly (setRetries -> retries).
- Cover delegate field injection in unit tests.
When it happens
Trigger: A delegate declares <flowable:field name='x'><flowable:string value='...'/></flowable:field> (or expression) and the generated value type (String, Expression, etc.) is not assignable to the setter parameter, so Method.invoke raises IllegalArgumentException.
Common situations: Setter expects int/boolean but the field is declared as a string; setter takes a concrete type while Flowable injects an Expression; mismatch between field name and the declared property's type; boxed vs primitive mismatch.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Error while invoking '%s' on class %s
- Illegal access when calling '%s' on class %s
- Exception while invoking '%s' on class %s
- Error while invoking '%s' on class %s
- ${className} does not implement the ${CmmnTriggerableActivit
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/f9ae768839d0b974.
Report an issue: GitHub.