{"record":{"id":"7a4939b909e84b72","repo":"flowable/flowable-engine","slug":"illegal-access-when-calling-s-on-class-s","errorCode":null,"errorMessage":"Illegal access when calling '%s' on class %s","messagePattern":"Illegal access when calling '(.+?)' on class (.+?)","errorType":"exception","errorClass":"ActivitiException","httpStatus":null,"severity":"error","filePath":"modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/helper/ClassDelegate.java","lineNumber":273,"sourceCode":"            }\n        }\n    }\n\n    public static void applyFieldDeclaration(FieldDeclaration declaration, Object target) {\n        applyFieldDeclaration(declaration, target, true);\n    }\n\n    public static void applyFieldDeclaration(FieldDeclaration declaration, Object target, boolean throwExceptionOnMissingField) {\n        Method setterMethod = ReflectUtil.getSetter(declaration.getName(),\n                target.getClass(), declaration.getValue().getClass());\n\n        if (setterMethod != null) {\n            try {\n                setterMethod.invoke(target, declaration.getValue());\n            } catch (IllegalArgumentException e) {\n                throw new ActivitiException(\"Error while invoking '\" + declaration.getName() + \"' on class \" + target.getClass().getName(), e);\n            } catch (IllegalAccessException e) {\n                throw new ActivitiException(\"Illegal access when calling '\" + declaration.getName() + \"' on class \" + target.getClass().getName(), e);\n            } catch (InvocationTargetException e) {\n                throw new ActivitiException(\"Exception while invoking '\" + declaration.getName() + \"' on class \" + target.getClass().getName(), e);\n            }\n        } else {\n            Field field = ReflectUtil.getField(declaration.getName(), target);\n            if (field == null) {\n                if (throwExceptionOnMissingField) {\n                    throw new ActivitiIllegalArgumentException(\"Field definition uses unexisting field '\" + declaration.getName() + \"' on class \" + target.getClass().getName());\n                } else {\n                    return;\n                }\n            }\n\n            // Check if the delegate field's type is correct\n            if (!fieldTypeCompatible(declaration, field)) {\n                throw new ActivitiIllegalArgumentException(\"Incompatible type set on field declaration '\" + declaration.getName()\n                        + \"' for class \" + target.getClass().getName()\n                        + \". Declared value has type \" + declaration.getValue().getClass().getName()","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/helper/ClassDelegate.java#L255-L291","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nvoid setRecipient(String recipient) { this.recipient = recipient; }\n// after\npublic void setRecipient(String recipient) { this.recipient = recipient; }","handlingStrategy":"validation","validationCode":"Method m = delegate.getClass().getMethod(\"set\" + capitalize(fieldName), valueType);\nif (!Modifier.isPublic(m.getModifiers())) throw new IllegalStateException(fieldName + \" setter must be public\");","typeGuard":"boolean hasPublicSetter(Class<?> c, String field, Class<?> type) {\n  try { return Modifier.isPublic(c.getMethod(\"set\" + Character.toUpperCase(field.charAt(0)) + field.substring(1), type).getModifiers()); }\n  catch (NoSuchMethodException e) { return false; }\n}","tryCatchPattern":"try {\n  applyFieldDeclaration(declaration, target, throwExceptionOnMissingField);\n} catch (ActivitiException e) {\n  if (e.getMessage().startsWith(\"Illegal access\")) {\n    log.error(\"Setter for field {} on {} is not accessible\", declaration.getName(), target.getClass(), e);\n  } else throw e;\n}","preventionTips":["Always declare delegate setters public","Avoid Lombok access levels below public on injected fields","Check SecurityManager/module settings when delegates live in isolated classloaders"],"tags":["reflection","field-injection","delegate","access-modifier"],"backgroundTag":"permission-denied","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}