{"record":{"id":"505c510adae64bcf","repo":"flowable/flowable-engine","slug":"exception-while-invoking-fieldname-on-class-c","errorCode":null,"errorMessage":"Exception while invoking '<fieldName>' on class <className>","messagePattern":"Exception while invoking '<fieldName>' on class <className>","errorType":"exception","errorClass":"FlowableException","httpStatus":null,"severity":"error","filePath":"modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/ClassDelegateUtil.java","lineNumber":59,"sourceCode":"        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(), 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 FlowableException(\"Error while invoking '\" + declaration.getName() + \"' on class \" + target.getClass().getName(), e);\n            } catch (IllegalAccessException e) {\n                throw new FlowableException(\"Illegal access when calling '\" + declaration.getName() + \"' on class \" + target.getClass().getName(), e);\n            } catch (InvocationTargetException e) {\n                throw new FlowableException(\"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 FlowableIllegalArgumentException(\"Field definition uses non-existing 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 FlowableIllegalArgumentException(\"Incompatible type set on field declaration '\" + declaration.getName() + \"' for class \" + target.getClass().getName() + \". Declared value has type \"\n                        + declaration.getValue().getClass().getName() + \", while expecting \" + field.getType().getName());\n            }\n            ReflectUtil.setField(field, target, declaration.getValue());\n        }\n    }\n\n    public static boolean fieldTypeCompatible(FieldDeclaration declaration, Field field) {\n        if (declaration.getValue() != null) {\n            return field.getType().isAssignableFrom(declaration.getValue().getClass());","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/ClassDelegateUtil.java#L41-L77","documentation":"Thrown by ClassDelegateUtil.applyFieldDeclaration when the reflective setter call throws an InvocationTargetException, i.e. the invoked setter itself raised an exception internally. Flowable wraps it in a FlowableException naming the field and target class; the root cause is in the wrapped exception.","triggerScenarios":"A BPMN field injection resolves to a setter that executes and throws (e.g. IllegalArgumentException from parsing, NullPointerException inside the setter body) while applyFieldDeclaration runs setterMethod.invoke(target, declaration.getValue()).","commonSituations":"Setter performs validation or parsing of the injected string/expression value and rejects it; injected expression evaluates to an unexpected value; constructor-time state missing when setter runs.","solutions":["Inspect the wrapped InvocationTargetException cause in the stack trace to find the real failure inside the setter","Fix the throwing logic in the setter of the delegate class","Correct the field value/expression in the BPMN XML so the setter accepts it","Add validation of injected values before assignment in the setter with a descriptive message"],"exampleFix":"// before\npublic void setTimeout(String t) { this.timeout = Integer.parseInt(t); }\n// after\npublic void setTimeout(String t) {\n    try { this.timeout = Integer.parseInt(t); }\n    catch (NumberFormatException e) { throw new IllegalArgumentException(\"timeout must be a number, got: \" + t, e); }\n}","handlingStrategy":"try-catch","validationCode":"// Smoke-instantiate and invoke the setter with the declared value before deployment\nObject d = delegateClass.getDeclaredConstructor().newInstance();\ndelegateClass.getMethod(\"set\" + cap(field), String.class).invoke(d, declaredValue);","typeGuard":"static boolean setterSafe(Object target, String field, Object value) {\n    try { target.getClass().getMethod(\"set\" + cap(field)).invoke(target, value); return true; }\n    catch (Exception e) { return false; }\n}","tryCatchPattern":"try { runtimeService.startProcessInstanceByKey(key); }\ncatch (FlowableException e) {\n    if (e.getCause() instanceof InvocationTargetException) {\n        Throwable root = e.getCause().getCause();\n        log.error(\"Setter for {} failed: {}\", e.getMessage(), root, root);\n    } else throw e;\n}","preventionTips":["Keep setters trivial (assign-only); do parsing/validation elsewhere","Unit test every delegate with its declared field values","Log the full cause chain, the real error is inside InvocationTargetException"],"tags":["java","reflection","field-injection","setter-exception"],"backgroundTag":"invalid-argument-value","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"}