flowable/flowable-engine · error · ActivitiIllegalArgumentException
Value is not a list of JPA entities: " + value
Error message
Value is not a list of JPA entities: " + value
What it means
Thrown by JPAEntityListVariableType.setValue() when the value being stored is neither null nor a List whose elements the type can treat as JPA entities. The type guards the shape of the incoming value before serialization and rejects anything else with ActivitiIllegalArgumentException.
Solutions
- Store single entities with JPAEntityVariableType (plain entity variable) instead of the list type
- Ensure the value is a java.util.List of JPA-managed entity objects
- Check variable-type resolution order if a custom type is shadowing the intended handler
- Convert non-entity objects (DTOs) to JSON/serializable variables instead of JPA types
Example fix
// before runtimeService.setVariable(executionId, "orders", singleOrder); // list type, single entity // after runtimeService.setVariable(executionId, "orders", List.of(order1, order2));
Defensive patterns
Strategy: type-guard
Validate before calling
// before setting the variable
if (!(value instanceof List) || ((List<?>) value).isEmpty() || !entityClass.isInstance(((List<?>) value).get(0))) {
throw new IllegalArgumentException("Expected a List of JPA entities");
} Type guard
boolean isJpaEntityList(Object v) {
return v instanceof List && !((List<?>) v).isEmpty()
&& entityManager.getMetamodel().getEntities().stream()
.anyMatch(t -> t.getJavaType().isInstance(((List<?>) v).get(0)));
} Try / catch
try {
runtimeService.setVariable(executionId, "orders", orderList);
} catch (ActivitiIllegalArgumentException e) {
if (e.getMessage().startsWith("Value is not a list of JPA entities")) {
// handle single-entity vs list mismatch
}
throw e;
} Prevention
- Use JPAEntityVariableType for single entities and JPAEntityListVariableType only for lists
- Never rely on generics to enforce element types at runtime
- Convert DTOs to dedicated (JSON/serializable) variables, not JPA types
When it happens
Trigger: Calling runtimeService.setVariable(name, someObject) where someObject is a single entity, a String, or a non-entity list while JPAEntityListVariableType is selected as the variable type; passing a list of non-Serializable/non-entity objects.
Common situations: Passing a single JPA entity where a list type is expected (single entities go through JPAEntityVariableType); list elements not being entities at all; generics erasure hiding the wrong element type until runtime.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Cannot set JPA variable: " + EntityManagerSession.class + "…
- Deserialized value is not an array of ID's
- JPA entity variables can only be used in…
- Unexpected exception when serializing JPA id's
- Value is not a list of JPA entities:
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/2c20d5e11e20cea6.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/variable/JPAEntityListVariableType.java:119
if (value instanceof List<?> && ((List<?>) value).size() > 0) {
List<?> list = (List<?>) value;
List<String> ids = new ArrayList<>();
String type = mappings.getJPAClassString(list.get(0));
for (Object entry : list) {
ids.add(mappings.getJPAIdString(entry));
}
// Store type in text field and the ID's as a serialized array
valueFields.setBytes(serializeIds(ids));
valueFields.setTextValue(type);
} else if (value == null) {
valueFields.setBytes(null);
valueFields.setTextValue(null);
} else {
throw new ActivitiIllegalArgumentException("Value is not a list of JPA entities: " + value);
}
}
@Override
public Object getValue(ValueFields valueFields) {
byte[] bytes = valueFields.getBytes();
if (valueFields.getTextValue() != null && bytes != null) {
String entityClass = valueFields.getTextValue();
List<Object> result = new ArrayList<>();
String[] ids = deserializeIds(bytes);
for (String id : ids) {
result.add(mappings.getJPAEntity(entityClass, id));
}
return result;View on GitHub (pinned to d6d39ce1c6)