flowable/flowable-engine · error · ActivitiException

Illegal access when calling

Error message

Illegal access when calling '%s' on class %s

What it means

Thrown when applying a BPMN field injection declaration to a service task delegate via a setter method: the underlying setter's Method.invoke() raised IllegalAccessException, meaning the setter is not accessible (e.g. non-public) from the engine's reflection call. The engine wraps it in an ActivitiException naming the field and target class.

Solutions

  1. Make the setter method public on the delegate class
  2. Verify the flowable:field name attribute exactly matches an existing public setter (setXxx)
  3. Alternatively switch to direct field injection: make the target field public or use field name matching a public field
  4. Check the target class is the actual delegate class (not a subclass without the setter)

Example fix

// before
public class MyDelegate implements JavaDelegate {
    private String value;
    void setValue(String value) { this.value = value; }
}
// after
public class MyDelegate implements JavaDelegate {
    private String value;
    public void setValue(String value) { this.value = value; }
}
Defensive patterns

Strategy: validation

Validate before calling

if (java.lang.reflect.Modifier.isPublic(delegate.getClass().getMethod("setValue", String.class).getModifiers())) { /* safe to inject */ }

Prevention

When it happens

Trigger: A delegate class has a setter for an injected field declared in the BPMN XML (flowable:field) that is private/protected/package-private or otherwise not callable via reflection, and ClassDelegateUtil.applyFieldDeclaration invokes it.

Common situations: Hand-written delegates with non-public setters; copying delegate code between packages with different visibility; refactoring a public field into a private setter without making the setter public; proxies or restricted classloaders blocking access.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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

Appendix: source

Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/helper/ClassDelegateUtil.java:58

    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 ActivitiException("Error while invoking '" + declaration.getName() + "' on class " + target.getClass().getName(), e);
            } catch (IllegalAccessException e) {
                throw new ActivitiException("Illegal access when calling '" + declaration.getName() + "' on class " + target.getClass().getName(), e);
            } catch (InvocationTargetException e) {
                throw new ActivitiException("Exception while invoking '" + declaration.getName() + "' on class " + target.getClass().getName(), e);
            }
        } else {
            Field field = ReflectUtil.getField(declaration.getName(), target);
            if (field == null) {
                throw new ActivitiIllegalArgumentException("Field definition uses unexisting field '" + declaration.getName() + "' on class " + target.getClass().getName());
            }
            // Check if the delegate field's type is correct
            if (!fieldTypeCompatible(declaration, field)) {
                throw new ActivitiIllegalArgumentException("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)