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

  1. Make the referenced bean implement FlowableCollectionHandler (provide getCollection(DelegateExecution))
  2. Point the delegate expression at a bean that actually implements FlowableCollectionHandler
  3. Verify the resolved object's runtime type (watch for proxies that mask the interface)
  4. 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

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


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)