flowable/flowable-engine · error · FlowableException
Illegal access when calling '<fieldName>' on class <classNam
Error message
Illegal access when calling '<fieldName>' on class <className>
What it means
Thrown by ClassDelegateUtil.applyFieldDeclaration when a field-injection setter method invoked reflectively via Method.invoke throws an IllegalAccessException, meaning the setter is not accessible from the calling context (e.g. a non-public setter without setAccessible). Flowable wraps the reflective failure in a FlowableException naming the field and target class.
Source
Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/ClassDelegateUtil.java:57
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());
}
}
public static boolean fieldTypeCompatible(FieldDeclaration declaration, Field field) {View on GitHub (pinned to d6d39ce1c6)
Solutions
- Make the injected field's setter method public (and the class public)
- Use a public field with direct field injection instead of a setter
- Check that no security manager / JPMS module settings block setAccessible on the class
- Catch FlowableException in your delegate configuration and log target class + field name to locate the offending setter
Example fix
// before
public class MyDelegate implements JavaDelegate {
private void setUrl(String url) { this.url = url; }
}
// after
public class MyDelegate implements JavaDelegate {
private String url;
public void setUrl(String url) { this.url = url; }
} Defensive patterns
Strategy: try-catch
Validate before calling
Class<?> c = delegate.getClass();
for (FieldDeclaration d : fields) {
boolean hasPublicSetter = Arrays.stream(c.getMethods())
.anyMatch(m -> m.getName().equals("set" + Character.toUpperCase(d.getName().charAt(0)) + d.getName().substring(1))
&& Modifier.isPublic(m.getModifiers()));
if (!hasPublicSetter) throw new IllegalStateException("No public setter for field " + d.getName());
} Type guard
static boolean hasAccessibleSetter(Object target, String field) {
try { target.getClass().getMethod("set" + Character.toUpperCase(field.charAt(0)) + field.substring(1)); return true; }
catch (NoSuchMethodException e) { return false; }
} Try / catch
try { engineService.startProcessInstanceByKey(key, vars); }
catch (FlowableException e) {
if (e.getMessage() != null && e.getMessage().startsWith("Illegal access when calling")) {
log.error("Field injection setter not accessible: {}", e.getMessage());
} else throw e;
} Prevention
- Always declare injected fields as private with public setters or public visibility
- Test delegate field injection in unit tests before deploying processes
- Avoid JPMS/security-manager setups that restrict reflection on delegate classes
When it happens
Trigger: A BPMN field injection declares a field whose class exposes a setter that is private/protected or otherwise inaccessible, so setterMethod.invoke(target, declaration.getValue()) fails with IllegalAccessException.
Common situations: Custom JavaDelegate/service-task classes with non-public setters used with <flowable:field> declarations; class-loader or module access restrictions (JPMS, OSGi) blocking reflection; copying delegate code into a different package and shrinking visibility.
Understand the failure class
Background: "You do not have permission" / 403 Forbidden errors: authenticated but not allowed — causes and fixes across open-source libraries — this error's family across 31 libraries.
Related errors
- Field definition uses non-existent field '${name}' of class
- Incompatible type set on field declaration '${name}' for cla
- Could not set field ${field}
- Exception while invoking '<fieldName>' on class <className>
- Illegal access when calling '%s' on class %s
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/f3318f43d468db49.
Report an issue: GitHub.