{"record":{"id":"139f3ccdbe275c2b","repo":"flowable/flowable-engine","slug":"incompatible-type-set-on-field-declaration-s-fo","errorCode":null,"errorMessage":"Incompatible type set on field declaration '%s' for class %s. Declared value has type %s, while expecting %s","messagePattern":"Incompatible type set on field declaration '(.+?)' for class (.+?)\\. Declared value has type (.+?), while expecting (.+?)","errorType":"validation","errorClass":"ActivitiIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/helper/ClassDelegate.java","lineNumber":289,"sourceCode":"                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                if (throwExceptionOnMissingField) {\n                    throw new ActivitiIllegalArgumentException(\"Field definition uses unexisting field '\" + declaration.getName() + \"' on class \" + target.getClass().getName());\n                } else {\n                    return;\n                }\n            }\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        }\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","sourceCodeStart":271,"sourceCodeEnd":307,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/helper/ClassDelegate.java#L271-L307","documentation":"After locating the target field for direct injection, ClassDelegate validates that the declared value's type is assignable to the field's declared type via fieldTypeCompatible(). If not, it throws ActivitiIllegalArgumentException with both type names. This prevents ClassCastException later when the delegate reads the field.","triggerScenarios":"A field declaration sets a value whose Java type is incompatible with the delegate's field type - e.g. injecting a string value into a field of type int, Date, Expression, or a custom POJO where no conversion applies.","commonSituations":"activiti:field with <activiti:string> into an int/long/boolean field; injecting an expression string into an Expression-typed field without the expected wrapper; schema/version change of delegate field type while old process XML is reused.","solutions":["Change the declared value type in the BPMN XML so it matches the field type (e.g. use activiti:expression or the correct element).","Widen the delegate field type (e.g. accept Expression or String and convert in execute()).","Add a setter with a compatible parameter type so setter injection is used instead of direct field injection."],"exampleFix":"// before\n<activiti:field name=\"retries\"><activiti:string>3</activiti:string></activiti:field>\n// delegate field: private int retries;\n// after\n<activiti:field name=\"retries\"><activiti:expression>3</activiti:expression></activiti:field>\n// or change field to: private String retries;","handlingStrategy":"validation","validationCode":"Field f = delegateClass.getDeclaredField(fieldName);\nif (!fieldTypeCompatible(declaredValue.getClass(), f.getType())) {\n  throw new IllegalStateException(\"Declared \" + declaredValue.getClass() + \" incompatible with \" + f.getType());\n}","typeGuard":"boolean fieldTypeCompatible(Object value, Field field) {\n  return value == null || field.getType().isInstance(value) || Expression.class.isAssignableFrom(field.getType());\n}","tryCatchPattern":"try {\n  applyFieldDeclaration(declaration, target, throwExceptionOnMissingField);\n} catch (ActivitiIllegalArgumentException e) {\n  if (e.getMessage().contains(\"Incompatible type\")) {\n    log.error(\"Field '{}' type mismatch on {}\", declaration.getName(), target.getClass(), e);\n  } else throw e;\n}","preventionTips":["Match activiti:field child element (string/expression/dom) to the field's Java type","Prefer Expression or String field types for flexibility","Add deployment tests injecting real field declarations into delegates"],"tags":["field-injection","delegate","type-mismatch","configuration"],"backgroundTag":"type-mismatch","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-18T11:17:12.947Z"}