flowable/flowable-engine · error · FlowableException
Error while invoking
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.
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
- Error while invoking
- Exception while invoking
- Illegal access when calling
- Builder is missing constructor (can't pass features)
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/f9ae768839d0b974.
Report an issue: GitHub.
Appendix: 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)