flowable/flowable-engine · error · ActivitiIllegalArgumentException
Incompatible type set on field declaration
Error message
Incompatible type set on field declaration '%s' for class %s. Declared value has type %s, while expecting %s
What it means
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.
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.
Example fix
// before <activiti:field name="retries"><activiti:string>3</activiti:string></activiti:field> // delegate field: private int retries; // after <activiti:field name="retries"><activiti:expression>3</activiti:expression></activiti:field> // or change field to: private String retries;
Defensive patterns
Strategy: validation
Validate before calling
Field f = delegateClass.getDeclaredField(fieldName);
if (!fieldTypeCompatible(declaredValue.getClass(), f.getType())) {
throw new IllegalStateException("Declared " + declaredValue.getClass() + " incompatible with " + f.getType());
} Type guard
boolean fieldTypeCompatible(Object value, Field field) {
return value == null || field.getType().isInstance(value) || Expression.class.isAssignableFrom(field.getType());
} Try / catch
try {
applyFieldDeclaration(declaration, target, throwExceptionOnMissingField);
} catch (ActivitiIllegalArgumentException e) {
if (e.getMessage().contains("Incompatible type")) {
log.error("Field '{}' type mismatch on {}", declaration.getName(), target.getClass(), e);
} else throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Error while invoking
- Error while invoking
- Field definition uses unexisting field
- completed() can only be called on a
- completing() can only be called on a
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/139f3ccdbe275c2b.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/helper/ClassDelegate.java:289
throw new ActivitiException("Error while invoking '" + declaration.getName() + "' on class " + target.getClass().getName(), e);
} catch (IllegalAccessException e) {
throw new ActivitiException("Illegal access when calling '" + declaration.getName() + "' on class " + target.getClass().getName(), e);
} catch (InvocationTargetException e) {
throw new ActivitiException("Exception while invoking '" + declaration.getName() + "' on class " + target.getClass().getName(), e);
}
} else {
Field field = ReflectUtil.getField(declaration.getName(), target);
if (field == null) {
if (throwExceptionOnMissingField) {
throw new ActivitiIllegalArgumentException("Field definition uses unexisting field '" + declaration.getName() + "' on class " + target.getClass().getName());
} else {
return;
}
}
// Check if the delegate field's type is correct
if (!fieldTypeCompatible(declaration, field)) {
throw new ActivitiIllegalArgumentException("Incompatible type set on field declaration '" + declaration.getName()
+ "' for class " + target.getClass().getName()
+ ". Declared value has type " + declaration.getValue().getClass().getName()
+ ", while expecting " + field.getType().getName());
}
ReflectUtil.setField(field, target, declaration.getValue());
}
}
public static boolean fieldTypeCompatible(FieldDeclaration declaration, Field field) {
if (declaration.getValue() != null) {
return field.getType().isAssignableFrom(declaration.getValue().getClass());
} else {
// Null can be set any field type
return true;
}
}
View on GitHub (pinned to d6d39ce1c6)