flowable/flowable-engine · error · FlowableIllegalArgumentException
Delegate expression
Error message
Delegate expression
What it means
The plan item's delegateExpression resolved to an object implementing none of the four supported interfaces: PlanItemActivityBehavior, CmmnActivityBehavior, PlanItemJavaDelegate, or PlanItemFutureJavaDelegate. Flowable does not know how to execute the object, so it throws FlowableIllegalArgumentException listing the accepted interfaces.
Solutions
- Make the delegate class implement org.flowable.cmmn.api.delegate.PlanItemJavaDelegate (or PlanItemFutureJavaDelegate for async work).
- If the class should control plan item lifecycle, implement PlanItemActivityBehavior; for generic behavior, CmmnActivityBehavior.
- Verify the expression resolves to the intended bean (check the delegateExpression value and Spring context).
- Catch FlowableIllegalArgumentException and log the resolved object's getClass() plus implemented interfaces to see what is missing.
Example fix
// before
public class MyWorker implements org.flowable.engine.delegate.JavaDelegate { ... }
// after
public class MyWorker implements org.flowable.cmmn.api.delegate.PlanItemJavaDelegate {
public Object execute(DelegatePlanItemInstance planItemInstance) { ... }
} Defensive patterns
Strategy: type-guard
Validate before calling
Object delegate = applicationContext.getBean(beanName);
boolean ok = delegate instanceof PlanItemActivityBehavior || delegate instanceof CmmnActivityBehavior
|| delegate instanceof PlanItemJavaDelegate || delegate instanceof PlanItemFutureJavaDelegate;
if (!ok) throw new IllegalArgumentException(beanName + " implements none of the supported CMMN delegate interfaces"); Type guard
boolean isValidCmmnDelegate(Object d) {
return d instanceof PlanItemActivityBehavior || d instanceof CmmnActivityBehavior
|| d instanceof PlanItemJavaDelegate || d instanceof PlanItemFutureJavaDelegate;
} Try / catch
try {
// start case / execute plan item
} catch (FlowableIllegalArgumentException e) {
if (e.getMessage().startsWith("Delegate expression")) {
log.error("Delegate for expression resolved to unsupported type: {}", e.getMessage());
}
} Prevention
- Use org.flowable.cmmn.api.delegate.PlanItemJavaDelegate, never the BPMN JavaDelegate, in CMMN cases
- Assert bean types in Spring configuration tests before deployment
- Keep delegate interfaces in one module to avoid accidental imports of the wrong package
When it happens
Trigger: execute() of PlanItemDelegateExpressionActivityBehavior, when DelegateExpressionUtil.resolveDelegateExpression returns an object whose class implements none of the four supported interfaces.
Common situations: Implementing the plain Flowable (BPMN) JavaDelegate interface instead of PlanItemJavaDelegate in a CMMN case; referencing a bean that is a helper/service class, not a delegate; refactoring renamed the interface the class implements; Spring bean wired to the wrong component.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Cannot complete CMMN job. There is no CMMN engine available
- Cannot query external jobs. There is no BPMN or CMMN engine…
- Cannot unacquire CMMN job. There is no CMMN engine available
- Case should be validated, but no case validator is…
- CommandInvoker must be the last interceptor in the chain
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/b9b1c513c64dd7f9.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/PlanItemDelegateExpressionActivityBehavior.java:78
} else if (delegate instanceof CmmnActivityBehavior) {
try {
((CmmnActivityBehavior) delegate).execute(planItemInstanceEntity);
} catch (BusinessError fault) {
FaultPropagation.propagateFault(fault, commandContext, planItemInstanceEntity);
}
} else if (delegate instanceof PlanItemJavaDelegate) {
// PlanItemJavaDelegateActivityBehavior already catches CmmnFault internally
PlanItemJavaDelegateActivityBehavior behavior = new PlanItemJavaDelegateActivityBehavior((PlanItemJavaDelegate) delegate);
behavior.execute(planItemInstanceEntity);
} else if (delegate instanceof PlanItemFutureJavaDelegate) {
// PlanItemFutureJavaDelegateActivityBehavior already catches CmmnFault internally
PlanItemFutureJavaDelegateActivityBehavior behavior = new PlanItemFutureJavaDelegateActivityBehavior((PlanItemFutureJavaDelegate<?>) delegate);
behavior.execute(planItemInstanceEntity);
} else {
throw new FlowableIllegalArgumentException("Delegate expression " + expression + " did neither resolve to an implementation of " +
PlanItemActivityBehavior.class + ", " + CmmnActivityBehavior.class + ", " + PlanItemJavaDelegate.class + " nor "
+ PlanItemFutureJavaDelegate.class);
}
}
@Override
public void trigger(CommandContext commandContext, PlanItemInstanceEntity planItemInstanceEntity) {
Expression expressionObject = CommandContextUtil.getCmmnEngineConfiguration(commandContext).getExpressionManager().createExpression(expression);
Object delegate = DelegateExpressionUtil.resolveDelegateExpression(expressionObject, planItemInstanceEntity, fieldExtensions);
if (delegate instanceof CmmnTriggerableActivityBehavior) { // includes CmmnTriggerableActivityBehavior
((CmmnTriggerableActivityBehavior) delegate).trigger(planItemInstanceEntity);
} else {
throw new FlowableIllegalArgumentException("Delegate expression " + expression + " did neither resolve to an implementation of "
+ CmmnTriggerableActivityBehavior.class);
}
}
View on GitHub (pinned to d6d39ce1c6)