{"record":{"id":"f9ae768839d0b974","repo":"flowable/flowable-engine","slug":"error-while-invoking-fieldname-on-class-class","errorCode":null,"errorMessage":"Error while invoking '<fieldName>' on class <className>","messagePattern":"Error 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":55,"sourceCode":"        return object;\n    }\n\n    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(), 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    }","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/ClassDelegateUtil.java#L37-L73","documentation":"ClassDelegateUtil.applyFieldDeclaration injects <field> values declared in BPMN into the delegate instance via a setter found with ReflectUtil. When the setter exists but invoking it throws IllegalArgumentException (argument type not acceptable to the method), a FlowableException wrapping it is thrown with the field name and class. This typically means the field value type in the XML does not match the setter's parameter type.","triggerScenarios":"A delegate declares <flowable:field name='x'><flowable:string value='...'/></flowable:field> (or expression) and the generated value type (String, Expression, etc.) is not assignable to the setter parameter, so Method.invoke raises IllegalArgumentException.","commonSituations":"Setter expects int/boolean but the field is declared as a string; setter takes a concrete type while Flowable injects an Expression; mismatch between field name and the declared property's type; boxed vs primitive mismatch.","solutions":["Align the setter's parameter type with what the BPMN field declaration produces (accept Expression for flowable:expression/string fields).","Declare the field in BPMN with the matching type element (flowable:string, flowable:expression) for the setter.","Change the field name so ReflectUtil resolves a setter whose parameter matches the value type.","Inspect the cause chain (the wrapped IllegalArgumentException) to see the exact type mismatch."],"exampleFix":"// before\npublic void setRetries(int retries) { ... }\n<!-- <flowable:field name=\"retries\"><flowable:string value=\"3\"/> </flowable:field> -->\n// after\npublic void setRetries(Expression retries) { ... }\n<!-- or declare a numeric field type; then retries.getValueAsInt(execution) -->","handlingStrategy":"try-catch","validationCode":"Method m = ReflectUtil.getSetter(field.getName(), delegateClass, value.getClass());\nif (m == null) throw new IllegalStateException(\"No matching setter for field \" + field.getName());","typeGuard":"boolean hasMatchingSetter(Class<?> target, String field, Object value) {\n    return ReflectUtil.getSetter(field, target, value.getClass()) != null;\n}","tryCatchPattern":"try {\n    runtimeService.startProcessInstanceByKey(\"proc\");\n} catch (FlowableException e) {\n    if (e.getMessage().startsWith(\"Error while invoking '\")) { /* fix field/setter types */ }\n    throw e;\n}","preventionTips":["Declare fields in BPMN with the type element matching the setter parameter.","Accept Expression for string/expression fields and resolve at execution time.","Match field names to setter names exactly (setRetries -> retries).","Cover delegate field injection in unit tests."],"tags":["flowable","delegate","field-injection","reflection"],"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"}