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

  1. Make the implementation class implement org.flowable.variable.api.aggregation.VariableAggregator (add aggregate/aggregateResult methods).
  2. Point the aggregation implementation at an existing VariableAggregator implementation instead.
  3. If using delegateExpression, ensure the resolved bean also implements VariableAggregator (the same check applies).
  4. 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

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


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)