flowable/flowable-engine · error · FlowableException

Exception while invoking '<fieldName>' on class <className>

Error message

Exception while invoking '<fieldName>' on class <className>

What it means

Thrown by ClassDelegateUtil.applyFieldDeclaration when the reflective setter call throws an InvocationTargetException, i.e. the invoked setter itself raised an exception internally. Flowable wraps it in a FlowableException naming the field and target class; the root cause is in the wrapped exception.

Source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/ClassDelegateUtil.java:59

        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());
        }
    }

    public static boolean fieldTypeCompatible(FieldDeclaration declaration, Field field) {
        if (declaration.getValue() != null) {
            return field.getType().isAssignableFrom(declaration.getValue().getClass());

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Inspect the wrapped InvocationTargetException cause in the stack trace to find the real failure inside the setter
  2. Fix the throwing logic in the setter of the delegate class
  3. Correct the field value/expression in the BPMN XML so the setter accepts it
  4. Add validation of injected values before assignment in the setter with a descriptive message

Example fix

// before
public void setTimeout(String t) { this.timeout = Integer.parseInt(t); }
// after
public void setTimeout(String t) {
    try { this.timeout = Integer.parseInt(t); }
    catch (NumberFormatException e) { throw new IllegalArgumentException("timeout must be a number, got: " + t, e); }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Smoke-instantiate and invoke the setter with the declared value before deployment
Object d = delegateClass.getDeclaredConstructor().newInstance();
delegateClass.getMethod("set" + cap(field), String.class).invoke(d, declaredValue);

Type guard

static boolean setterSafe(Object target, String field, Object value) {
    try { target.getClass().getMethod("set" + cap(field)).invoke(target, value); return true; }
    catch (Exception e) { return false; }
}

Try / catch

try { runtimeService.startProcessInstanceByKey(key); }
catch (FlowableException e) {
    if (e.getCause() instanceof InvocationTargetException) {
        Throwable root = e.getCause().getCause();
        log.error("Setter for {} failed: {}", e.getMessage(), root, root);
    } else throw e;
}

Prevention

When it happens

Trigger: A BPMN field injection resolves to a setter that executes and throws (e.g. IllegalArgumentException from parsing, NullPointerException inside the setter body) while applyFieldDeclaration runs setterMethod.invoke(target, declaration.getValue()).

Common situations: Setter performs validation or parsing of the injected string/expression value and rejects it; injected expression evaluates to an unexpected value; constructor-time state missing when setter runs.

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/505c510adae64bcf. Report an issue: GitHub.