{"record":{"id":"a79dd62e2e1fdd4f","repo":"flowable/flowable-engine","slug":"illegal-access-when-calling-s-on-class-s-a79dd6","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/ClassDelegateUtil.java","lineNumber":58,"sourceCode":"    public static void applyFieldDeclaration(List<FieldDeclaration> fieldDeclarations, Object target) {\n        if (fieldDeclarations != null) {\n            for (FieldDeclaration declaration : fieldDeclarations) {\n                applyFieldDeclaration(declaration, target);\n            }\n        }\n    }\n\n    public static void applyFieldDeclaration(FieldDeclaration declaration, Object target) {\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                throw new ActivitiIllegalArgumentException(\"Field definition uses unexisting field '\" + declaration.getName() + \"' on class \" + target.getClass().getName());\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()\n                        + \", while expecting \" + field.getType().getName());\n            }\n            ReflectUtil.setField(field, target, declaration.getValue());\n        }\n    }","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/helper/ClassDelegateUtil.java#L40-L76","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Make the setter method public on the delegate class","Verify the flowable:field name attribute exactly matches an existing public setter (setXxx)","Alternatively switch to direct field injection: make the target field public or use field name matching a public field","Check the target class is the actual delegate class (not a subclass without the setter)"],"exampleFix":"// before\npublic class MyDelegate implements JavaDelegate {\n    private String value;\n    void setValue(String value) { this.value = value; }\n}\n// after\npublic class MyDelegate implements JavaDelegate {\n    private String value;\n    public void setValue(String value) { this.value = value; }\n}","handlingStrategy":"validation","validationCode":"if (java.lang.reflect.Modifier.isPublic(delegate.getClass().getMethod(\"setValue\", String.class).getModifiers())) { /* safe to inject */ }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always declare injected-field setters public","Keep field names in BPMN XML in sync with delegate fields (IDE checks or unit test that applies declarations)","Write a test that instantiates each delegate and applies its field declarations"],"tags":["java","reflection","bpmn","field-injection"],"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"}