flowable/flowable-engine · error · ActivitiException
Illegal access when calling '%s' on class %s
Error message
Illegal access when calling '%s' on class %s
What it means
During setter-based field injection, Method.invoke can throw IllegalAccessException when the setter method is not accessible from the engine's reflection context (non-public setter, restrictive SecurityManager, or package-private class). ClassDelegate wraps it in this ActivitiException with the field and class names.
Source
Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/helper/ClassDelegate.java:273
}
}
}
public static void applyFieldDeclaration(FieldDeclaration declaration, Object target) {
applyFieldDeclaration(declaration, target, true);
}
public static void applyFieldDeclaration(FieldDeclaration declaration, Object target, boolean throwExceptionOnMissingField) {
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) {
if (throwExceptionOnMissingField) {
throw new ActivitiIllegalArgumentException("Field definition uses unexisting field '" + declaration.getName() + "' on class " + target.getClass().getName());
} else {
return;
}
}
// 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()View on GitHub (pinned to d6d39ce1c6)
Solutions
- Make the setter public on the delegate class.
- Alternatively rely on direct field injection by making the field non-final and accessible, ensuring fieldTypeCompatible passes.
- Check SecurityManager/classloader policy if the setter is already public.
Example fix
// before
void setRecipient(String recipient) { this.recipient = recipient; }
// after
public void setRecipient(String recipient) { this.recipient = recipient; } Defensive patterns
Strategy: validation
Validate before calling
Method m = delegate.getClass().getMethod("set" + capitalize(fieldName), valueType);
if (!Modifier.isPublic(m.getModifiers())) throw new IllegalStateException(fieldName + " setter must be public"); Type guard
boolean hasPublicSetter(Class<?> c, String field, Class<?> type) {
try { return Modifier.isPublic(c.getMethod("set" + Character.toUpperCase(field.charAt(0)) + field.substring(1), type).getModifiers()); }
catch (NoSuchMethodException e) { return false; }
} Try / catch
try {
applyFieldDeclaration(declaration, target, throwExceptionOnMissingField);
} catch (ActivitiException e) {
if (e.getMessage().startsWith("Illegal access")) {
log.error("Setter for field {} on {} is not accessible", declaration.getName(), target.getClass(), e);
} else throw e;
} Prevention
- Always declare delegate setters public
- Avoid Lombok access levels below public on injected fields
- Check SecurityManager/module settings when delegates live in isolated classloaders
When it happens
Trigger: The resolved delegate class has a setter that matches the field declaration name but is private/protected/package-private, or a security policy blocks reflective access to the member.
Common situations: Delegate class written with a non-public setter (Lombok @Setter(AccessLevel.PROTECTED), hand-written private setter); delegate deployed in a sealed/isolated classloader; Java module/SecurityManager restrictions in hardened environments.
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
- Error while invoking '<fieldName>' on class <className>
- Illegal access when calling '<fieldName>' on class <classNam
- Error while invoking '%s' on class %s
- Exception while invoking '%s' on class %s
- Error while invoking '%s' on class %s
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/7a4939b909e84b72.
Report an issue: GitHub.