{"record":{"id":"f03ac7a768ab85fb","repo":"flowable/flowable-engine","slug":"field-definition-uses-unexisting-field-s-on-cla","errorCode":null,"errorMessage":"Field definition uses unexisting field '%s' on class %s","messagePattern":"Field definition uses unexisting field '(.+?)' on class (.+?)","errorType":"validation","errorClass":"ActivitiIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/helper/ClassDelegate.java","lineNumber":281,"sourceCode":"    public static void applyFieldDeclaration(FieldDeclaration declaration, Object target, boolean throwExceptionOnMissingField) {\n        Method setterMethod = ReflectUtil.getSetter(declaration.getName(),\n                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 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) {","sourceCodeStart":263,"sourceCodeEnd":299,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/helper/ClassDelegate.java#L263-L299","documentation":"When no setter matches the field declaration name, ClassDelegate falls back to direct field injection. If neither a setter nor a field with that name exists on the delegate class and throwExceptionOnMissingField is true (default), an ActivitiIllegalArgumentException is thrown naming the missing field and class. This catches typos in activiti:field declarations at first use.","triggerScenarios":"BPMN XML declares <activiti:field name=\"foo\"> but the delegate class has neither setFoo(...) nor a member foo; ClassDelegate was constructed with throwExceptionOnMissingField=true.","commonSituations":"Typo in the field name in process XML; delegate refactored/renamed the field or setter while old process definitions still deployed; copying field declarations between delegates with different property names.","solutions":["Fix the field name in the BPMN XML (or delegateExpression config) to match an existing setter/field on the delegate class.","Add the missing setter/field to the delegate class.","If the field is intentionally optional, construct ClassDelegate with throwExceptionOnMissingField=false."],"exampleFix":"// before (BPMN)\n<activiti:field name=\"receipent\"><activiti:string>x</activiti:string></activiti:field>\n// after\n<activiti:field name=\"recipient\"><activiti:string>x</activiti:string></activiti:field>\n// matching: public void setRecipient(String recipient)","handlingStrategy":"validation","validationCode":"Class<?> c = delegateClass;\nboolean ok = Arrays.stream(c.getMethods()).anyMatch(m -> m.getName().equals(\"set\" + capitalize(fieldName)))\n          || Arrays.stream(c.getDeclaredFields()).anyMatch(f -> f.getName().equals(fieldName));\nif (!ok) throw new IllegalStateException(\"Field '\" + fieldName + \"' missing on \" + c.getName());","typeGuard":"boolean fieldExists(Class<?> c, String name) {\n  try { c.getDeclaredField(name); return true; }\n  catch (NoSuchFieldException e) {\n    return Arrays.stream(c.getMethods()).anyMatch(m -> m.getName().equalsIgnoreCase(\"set\" + name));\n  }\n}","tryCatchPattern":"try {\n  applyFieldDeclaration(declaration, target, true);\n} catch (ActivitiIllegalArgumentException e) {\n  if (e.getMessage().contains(\"unexisting field\")) {\n    log.error(\"activiti:field name '{}' does not exist on {}\", declaration.getName(), target.getClass(), e);\n  } else throw e;\n}","preventionTips":["Cross-check every activiti:field name against the delegate's setters/fields before deploying","Grep process XML field names in CI against delegate sources","Rename field and setter together and update all referencing process definitions"],"tags":["field-injection","delegate","configuration","typo"],"backgroundTag":"missing-required-config-field","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"}