flowable/flowable-engine · error · FlowableIllegalArgumentException
Class does not implement
Error message
Class ${implementation} does not implement ${VariableAggregator.class} What it means
BpmnAggregation.resolveVariableAggregator instantiates the class named by the aggregation definition and requires it to implement org.flowable.variable.api.aggregation.VariableAggregator. When the class-delegate implementation instantiates to something else, this FlowableIllegalArgumentException is thrown.
Solutions
- Make the implementation class implement org.flowable.variable.api.aggregation.VariableAggregator (add aggregate/aggregateResult methods).
- Point the aggregation implementation at an existing VariableAggregator implementation instead.
- If using delegateExpression, ensure the resolved bean also implements VariableAggregator (the same check applies).
- Verify the Flowable version: older/custom aggregator interfaces differ; import the one from flowable-variable-service (org.flowable.variable.api.aggregation).
Example fix
// before
public class MyAggregator implements JavaDelegate { ... }
// after
public class MyAggregator implements VariableAggregator {
public Object aggregate(VariableScopeScope, VariableAggregationContext ctx) { ... }
public Object aggregateResult(...) { ... }
} Defensive patterns
Strategy: validation
Validate before calling
Class<?> c = Class.forName(implClassName); if (!VariableAggregator.class.isAssignableFrom(c)) throw new IllegalArgumentException(implClassName + " must implement VariableAggregator");
Type guard
boolean isAggregator(Class<?> c) { return VariableAggregator.class.isAssignableFrom(c); } Try / catch
try { /* aggregation handling */ } catch (FlowableIllegalArgumentException e) { if (e.getMessage().contains("does not implement")) log.error("Aggregation misconfig: {}", e.getMessage()); else throw e; } Prevention
- Point aggregation definitions only at VariableAggregator implementations
- Add a deployment-time check that aggregation classes implement the interface
- Watch imports: use org.flowable.variable.api.aggregation.VariableAggregator, not similar names from other libs
When it happens
Trigger: A collection variable aggregation definition with implementationType 'class' whose implementation class does not implement VariableAggregator; resolveVariableAggregator called via aggregator during aggregated variable handling.
Common situations: Pointing the aggregation at a generic Delegate/JavaDelegate class by copy-paste; refactoring that removed the interface; wrong import of a similarly named aggregator interface from another package/version.
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
- Custom properties resolver delegate expression " +…
- Delegate expression " + expression + " did not resolve to…
- Delegate expression " + expression + " did not resolve to…
- Delegate expression " + expression + " did not resolve to…
- Delegate expression " + expression + " did not resolve to…
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/73c459074e87aa17.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/variable/BpmnAggregation.java:252
variableInstance.setScopeId(scopeId);
variableInstance.setSubScopeId(subScopeId);
variableInstance.setScopeType(ScopeTypes.BPMN_VARIABLE_AGGREGATION);
variableServiceConfiguration.getVariableInstanceValueModifier().setVariableValue(variableInstance, value, tenantId);
return variableInstance;
}
public static Map<String, List<VariableInstance>> groupVariableInstancesByName(List<? extends VariableInstance> instances) {
return instances.stream().collect(Collectors.groupingBy(VariableInstance::getName));
}
public static VariableAggregator resolveVariableAggregator(VariableAggregationDefinition aggregation, DelegateExecution execution) {
if (ImplementationType.IMPLEMENTATION_TYPE_CLASS.equalsIgnoreCase(aggregation.getImplementationType())) {
Object delegate = ClassDelegateUtil.instantiateDelegate(aggregation.getImplementation(), null);
if (delegate instanceof VariableAggregator) {
return (VariableAggregator) delegate;
}
throw new FlowableIllegalArgumentException("Class " + aggregation.getImplementation() + " does not implement " + VariableAggregator.class);
} else if (ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION.equalsIgnoreCase(aggregation.getImplementationType())) {
Object delegate = DelegateExpressionUtil.resolveDelegateExpression(
CommandContextUtil.getProcessEngineConfiguration().getExpressionManager().createExpression(aggregation.getImplementation()), execution);
if (delegate instanceof VariableAggregator) {
return (VariableAggregator) delegate;
}
throw new FlowableIllegalArgumentException(
"Delegate expression " + aggregation.getImplementation() + " did not resolve to an implementation of " + VariableAggregator.class);
} else {
return CommandContextUtil.getProcessEngineConfiguration().getVariableAggregator();
}
}
public static void sortVariablesByCounter(List<VariableInstance> variableInstances, List<VariableInstance> counterVariableInstances) {
if (counterVariableInstances == null || counterVariableInstances.isEmpty()) {
return;View on GitHub (pinned to d6d39ce1c6)