flowable/flowable-engine · error · FlowableIllegalArgumentException
doesn't implement FlowableCollectionHandler
Error message
<delegateClassName> doesn't implement FlowableCollectionHandler
What it means
Thrown by DelegateExpressionCollectionHandler.getCollectionHandlerInstance as a FlowableIllegalArgumentException when a delegate expression configured for a multi-instance/collection handler resolves to an object that does not implement FlowableCollectionHandler. Flowable cannot use the resolved bean as a collection handler, so it fails fast.
Solutions
- Make the referenced bean implement FlowableCollectionHandler (provide getCollection(DelegateExecution))
- Point the delegate expression at a bean that actually implements FlowableCollectionHandler
- Verify the resolved object's runtime type (watch for proxies that mask the interface)
- Check the correct Flowable module/API is on the classpath so the interface matches
Example fix
// before
public class MyBean { public List<String> getItems() { ... } }
// after
public class MyBean implements FlowableCollectionHandler {
public List<String> getCollection(DelegateExecution execution) { ... }
} Defensive patterns
Strategy: type-guard
Validate before calling
Object bean = applicationContext.getBean(beanName);
if (!(bean instanceof FlowableCollectionHandler)) {
throw new IllegalStateException(beanName + " must implement FlowableCollectionHandler");
} Type guard
static boolean isCollectionHandler(Expression expr, DelegateExecution exec) {
Object d = DelegateExpressionUtil.resolveDelegateExpression(expr, exec);
return d instanceof FlowableCollectionHandler;
} Try / catch
try { startProcess(key); }
catch (FlowableIllegalArgumentException e) {
if (e.getMessage().endsWith("doesn't implement " + FlowableCollectionHandler.class.getName())) {
log.error("Bad collection handler delegate: {}", e.getMessage());
} else throw e;
} Prevention
- Make all collection-handler beans implement FlowableCollectionHandler explicitly
- Assert bean types at application startup
- Avoid AOP proxies that hide the implemented interface where possible
When it happens
Trigger: A collection handler delegate expression (e.g. ${myBean}) resolves a Spring/CDI bean whose class does not implement org.flowable.engine.impl...FlowableCollectionHandler, during resolveCollection.
Common situations: Wiring an arbitrary bean or a JavaDelegate where a FlowableCollectionHandler is expected; renamed/moved interface after a Flowable version upgrade; bean returning a proxy/wrapper of the wrong type; copy-pasted delegate expression from another task.
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 + expression + did not resolve to an…
- Class does not implement FlowableEventListener
- Delegate expression did not resolve to an implementation of
- Expected an activity behavior in flow node
- A process instance id is required, but the provided id '" +…
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/bfd16249c0763e01.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/DelegateExpressionCollectionHandler.java:48
protected Expression expression;
public DelegateExpressionCollectionHandler(DelegateExecution execution, Expression expression) {
this.execution = execution;
this.expression = expression;
}
@Override
@SuppressWarnings("rawtypes")
public Collection resolveCollection(Object collectionValue, DelegateExecution execution) {
return getCollectionHandlerInstance(execution).resolveCollection(collectionValue, execution);
}
protected FlowableCollectionHandler getCollectionHandlerInstance(DelegateExecution execution) {
Object delegateInstance = DelegateExpressionUtil.resolveDelegateExpression(expression, execution);
if (delegateInstance instanceof FlowableCollectionHandler) {
return (FlowableCollectionHandler) delegateInstance;
} else {
throw new FlowableIllegalArgumentException(delegateInstance.getClass().getName() + " doesn't implement " + FlowableCollectionHandler.class);
}
}
}
View on GitHub (pinned to d6d39ce1c6)