{"record":{"id":"880b263c2c283ed9","repo":"flowable/flowable-engine","slug":"incompatible-type-set-on-field-declaration-name","errorCode":null,"errorMessage":"Incompatible type set on field declaration '${name}' for class ${target.getClass().getName()}. Declared value has type ${value.getClass().getName()}, while expecting ${field.getType().getName()}","messagePattern":"Incompatible type set on field declaration '(.+?)' for class (.+?)\\. Declared value has type (.+?), while expecting (.+?)","errorType":"validation","errorClass":"FlowableIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/util/ReflectUtil.java","lineNumber":181,"sourceCode":"    public static void invokeSetterOrField(Object target, String name, Object value, boolean throwExceptionOnMissingField) {\n        Method setterMethod = getSetter(name, target.getClass(), value.getClass());\n\n        if (setterMethod != null) {\n            invokeSetter(setterMethod, target, name, value);\n            \n        } else {\n            Field field = ReflectUtil.getField(name, target);\n            if (field == null) {\n                if (throwExceptionOnMissingField) {\n                    throw new FlowableIllegalArgumentException(\"Field definition uses non-existent field '\" + name + \"' of class \" + target.getClass().getName());\n                } else {\n                    return;\n                }\n            }\n\n            // Check if the delegate field's type is correct\n            if (!fieldTypeCompatible(value, field)) {\n                throw new FlowableIllegalArgumentException(\"Incompatible type set on field declaration '\" + name\n                        + \"' for class \" + target.getClass().getName()\n                        + \". Declared value has type \" + value.getClass().getName()\n                        + \", while expecting \" + field.getType().getName());\n            }\n            \n            setField(field, target, value);\n        }\n    }\n\n    /**\n     * Returns the field of the given object or null if it doesn't exist.\n     */\n    public static Field getField(String fieldName, Object object) {\n        return getField(fieldName, object.getClass());\n    }\n\n    /**\n     * Returns the field of the given class or null if it doesn't exist.","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/util/ReflectUtil.java#L163-L199","documentation":"After locating the target field, invokeSetterOrField checks that the value's type is assignable to the field's declared type via fieldTypeCompatible. If not, it throws this FlowableIllegalArgumentException detailing the value type and the expected field type.","triggerScenarios":"Injecting a value whose runtime type does not match the declared field type — e.g. a String field injection into an int/long field, a String into a custom bean field, or an expression that resolves to a different type than the field expects, in flowable:field definitions.","commonSituations":"BPMN field injection where the delegate declares int/boolean but the XML value is a string without the right type attribute; expression evaluating to the wrong type; changed field type after refactor.","solutions":["Align the field's declared type with the injected value type (e.g. change String field to int, or inject a String value).","In BPMN XML, set the correct type attribute on <flowable:field>/<flowable:value> (e.g. stringValue, expression).","For numeric fields, ensure the value resolves to the numeric type, or accept a String and parse it inside the delegate.","Update the process definition after refactoring field types.","Check both the value.getClass() and expected type printed in the message to pinpoint the mismatch."],"exampleFix":"// before\nprivate int retryLimit; // injected with <flowable:string value=\"10\"/>\n// after\n<flowable:field name=\"retryLimit\"><flowable:value>10</flowable:value></flowable:field> <!-- numeric injection -->\n// or change field to String and parse","handlingStrategy":"validation","validationCode":"Field f = target.getClass().getDeclaredField(name);\nif (!f.getType().isAssignableFrom(value.getClass())\n    && !(f.getType().isPrimitive() && isCompatiblePrimitive(f.getType(), value.getClass())))\n    throw new IllegalStateException(\"Value \" + value.getClass() + \" not assignable to \" + f.getType());","typeGuard":null,"tryCatchPattern":"try {\n    ReflectUtil.invokeSetterOrField(target, name, value, true);\n} catch (FlowableIllegalArgumentException e) {\n    LOGGER.error(\"Field {} type mismatch: {}\", name, e.getMessage());\n    throw new IllegalArgumentException(\"Fix injected value type for field \" + name, e);\n}","preventionTips":["Set explicit type attributes (stringValue, expression) on field injection elements","Keep field types and BPMN injection types in sync during refactors","Parse strings inside delegates rather than relying on auto-conversion","Test field injection with a delegate smoke test at deploy time"],"tags":["reflection","field-injection","type-mismatch","java"],"backgroundTag":"type-mismatch","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"}