{"record":{"id":"83037c58c4ad8afb","repo":"flowable/flowable-engine","slug":"incompatible-type-set-on-field-declaration-field","errorCode":null,"errorMessage":"Incompatible type set on field declaration '<fieldName>' for class <className>. Declared value has type <declaredType>, while expecting <expectedType>","messagePattern":"Incompatible type set on field declaration '<fieldName>' for class <className>\\. Declared value has type <declaredType>, while expecting <expectedType>","errorType":"validation","errorClass":"FlowableIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/ClassDelegateUtil.java","lineNumber":68,"sourceCode":"\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());\n        } else {\n            // Null can be set any field type\n            return true;\n        }\n    }\n\n}\n","sourceCodeStart":50,"sourceCodeEnd":85,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/ClassDelegateUtil.java#L50-L85","documentation":"Thrown by ClassDelegateUtil.applyFieldDeclaration as a FlowableIllegalArgumentException when the injected value's Java type is not compatible with the declared field's type (fieldTypeCompatible returns false). The message states both the declared value type and the expected field type.","triggerScenarios":"A <flowable:field> supplies a stringValue/expression whose runtime type (e.g. String) does not match the delegate field type (e.g. int, List, custom type), so the compatibility check fails before ReflectUtil.setField.","commonSituations":"Injecting a string into an Integer/List field without a matching type in the declaration; using expression results of unexpected type; switching a field's type in the delegate without updating process XML; confusion between stringValue, expression and object param types.","solutions":["Align the BPMN field declaration type with the delegate field type (use the correct stringValue/expression element)","Change the delegate field type to accept the injected value (e.g. String, or a type with a matching converter)","Use <flowable:field><flowable:value> with a serialized/custom value matching the field type","Add an explicit type check or conversion in the delegate's setter"],"exampleFix":"// before (field is int timeout)\n<flowable:field name=\"timeout\" stringValue=\"10\"/>\n// after\n<flowable:field name=\"timeout\"><flowable:value>10</flowable:value></flowable:field>\n// or change delegate field to String timeout","handlingStrategy":"validation","validationCode":"Field f = delegateClass.getDeclaredField(fieldName);\nObject v = resolveDeclaredValue(declaration);\nif (v != null && !f.getType().isAssignableFrom(v.getClass())) {\n    throw new ConfigException(\"Field \" + fieldName + \" expects \" + f.getType() + \" but got \" + v.getClass());\n}","typeGuard":"static boolean fieldTypeOk(Class<?> delegate, String name, Object value) throws NoSuchFieldException {\n    Class<?> t = delegate.getDeclaredField(name).getType();\n    return value == null || t.isInstance(value) || t.isPrimitive() && isBoxCompatible(t, value.getClass());\n}","tryCatchPattern":"try { startProcess(key, vars); }\ncatch (FlowableIllegalArgumentException e) {\n    if (e.getMessage().contains(\"Incompatible type set on field declaration\")) {\n        log.error(\"Field injection type mismatch: {}\", e.getMessage());\n    } else throw e;\n}","preventionTips":["Match stringValue/expression elements to the declared field type","Prefer String fields in delegates; convert types explicitly inside the delegate","Document expected field types on each JavaDelegate"],"tags":["java","field-injection","type-mismatch","bpmn"],"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"}