flowable/flowable-engine · error · FlowableIllegalArgumentException
Delegate expression did not resolve to an implementation of
Error message
Delegate expression ${aggregation.getImplementation()} did not resolve to an implementation of ${PlanItemVariableAggregator.class} What it means
resolveVariableAggregator looks up a delegate bean via the aggregation's delegate expression and requires it to implement PlanItemVariableAggregator. If the expression resolves to an object of the wrong type (or non-null but incompatible), Flowable throws this FlowableIllegalArgumentException because the CMMN engine cannot aggregate plan item variables with the given delegate.
Solutions
- Make the bean referenced by the delegate expression implement org.flowable.cmmn.engine.impl.variable.PlanItemVariableAggregator
- Verify the delegate expression points at the correct bean (print/log aggregation.getImplementation() and check the Spring context bean type)
- If no custom aggregation is needed, remove the delegate expression so the engine default variableAggregator from CmmnEngineConfiguration is used
- Rebuild/redeploy after fixing so the case definition picks up the corrected class
Example fix
// before
public class MyAggregator { public Object aggregate(...) {...} }
// after
public class MyAggregator implements PlanItemVariableAggregator {
public Object aggregate(PlanItemInstance planItemInstance, String variableName) {...}
} Defensive patterns
Strategy: validation
Validate before calling
Object delegate = beanResolver.resolve(aggregation.getImplementation());
if (!(delegate instanceof PlanItemVariableAggregator)) {
throw new IllegalStateException(aggregation.getImplementation() + " must implement PlanItemVariableAggregator");
} Type guard
boolean isAggregator(Object o) { return o instanceof PlanItemVariableAggregator; } Try / catch
try {
aggregator = CmmnAggregation.resolveVariableAggregator(...);
} catch (FlowableIllegalArgumentException e) {
logger.error("Aggregation delegate misconfigured: " + e.getMessage());
throw e;
} Prevention
- Have custom aggregators implement PlanItemVariableAggregator and add a unit test asserting the bean type
- Keep delegate expressions pointing at aggregator beans only
- Prefer omitting the delegate to use the engine default aggregator
When it happens
Trigger: A plan item's childRepetitionRule/variable aggregation declares a delegate expression (e.g. ${aggregation.getImplementation()}) that resolves to a bean not implementing PlanItemVariableAggregator; typo'd bean returning wrong type; custom aggregator class not implementing the interface.
Common situations: Copying a delegate class used elsewhere (e.g. a ValueProvider) into an aggregation role; upgrading Flowable where the aggregator interface changed; Spring bean wiring mistakes where the expression resolves to the factory or wrapper instead of the aggregator.
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
- Delegate expression did not resolve to an implementation of
- Delegate expression did not resolve to an implementation of
- Delegate expression did not resolve to an implementation of
- Delegate expression did not resolve to an implementation of
- Delegate expression did not resolve to an implementation of
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/d8ce4b20c019cb52.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/variable/CmmnAggregation.java:258
public static Map<String, List<VariableInstance>> groupVariableInstancesByName(List<? extends VariableInstance> instances) {
return instances.stream().collect(Collectors.groupingBy(VariableInstance::getName));
}
public static PlanItemVariableAggregator resolveVariableAggregator(VariableAggregationDefinition aggregation, DelegatePlanItemInstance planItemInstance) {
CmmnEngineConfiguration cmmnEngineConfiguration = CommandContextUtil.getCmmnEngineConfiguration();
if (ImplementationType.IMPLEMENTATION_TYPE_CLASS.equalsIgnoreCase(aggregation.getImplementationType())) {
return cmmnEngineConfiguration.getClassDelegateFactory().create(aggregation.getImplementation(), null);
} else if (ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION.equalsIgnoreCase(aggregation.getImplementationType())) {
Object delegate = DelegateExpressionUtil.resolveDelegateExpression(
cmmnEngineConfiguration.getExpressionManager().createExpression(aggregation.getImplementation()),
planItemInstance, null);
if (delegate instanceof PlanItemVariableAggregator) {
return (PlanItemVariableAggregator) delegate;
}
throw new FlowableIllegalArgumentException("Delegate expression " + aggregation.getImplementation() + " did not resolve to an implementation of " + PlanItemVariableAggregator.class);
} else {
return cmmnEngineConfiguration.getVariableAggregator();
}
}
public static void sortVariablesByCounter(List<VariableInstance> variableInstances, List<VariableInstance> counterVariableInstances) {
if (counterVariableInstances == null || counterVariableInstances.isEmpty()) {
return;
}
Map<String, Integer> sortOrder = new HashMap<>();
for (VariableInstance counterVariable : counterVariableInstances) {
Object value = counterVariable.getValue();
String[] values = value.toString().split(COUNTER_VAR_VALUE_SEPARATOR);
String variableInstanceId = values[0];
int order = Integer.parseInt(values[1]);
sortOrder.put(variableInstanceId, order);
}
View on GitHub (pinned to d6d39ce1c6)