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

  1. Align the setter's parameter type with what the BPMN field declaration produces (accept Expression for flowable:expression/string fields).
  2. Declare the field in BPMN with the matching type element (flowable:string, flowable:expression) for the setter.
  3. Change the field name so ReflectUtil resolves a setter whose parameter matches the value type.
  4. 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

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


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