flowable/flowable-engine · error · FlowableIllegalArgumentException
Delegate expression ${expression} did not resolve to an impl
Error message
Delegate expression ${expression} did not resolve to an implementation of ${PlanItemInstanceLifecycleListener.class} What it means
DelegateExpressionPlanItemLifecycleListener resolves a delegate expression against a plan item instance and requires the result to implement PlanItemInstanceLifecycleListener. Any other resolved type triggers FlowableIllegalArgumentException with the expression and expected interface. It protects the agenda/transition logic from calling stateChanged on an incompatible object.
Source
Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/listener/DelegateExpressionPlanItemLifecycleListener.java:59
@Override
public String getSourceState() {
return sourceState;
}
@Override
public String getTargetState() {
return targetState;
}
@Override
public void stateChanged(DelegatePlanItemInstance planItemInstance, String oldState, String newState) {
Object delegate = DelegateExpressionUtil.resolveDelegateExpression(expression, planItemInstance, fieldExtensions);
if (delegate instanceof PlanItemInstanceLifecycleListener listener) {
listener.stateChanged(planItemInstance, oldState, newState);
} else {
throw new FlowableIllegalArgumentException("Delegate expression " + expression + " did not resolve to an implementation of " + PlanItemInstanceLifecycleListener.class);
}
}
/**
* returns the expression text for this planItemInstance lifecycle listener.
*/
public String getExpressionText() {
return expression.getExpressionText();
}
}
View on GitHub (pinned to d6d39ce1c6)
Solutions
- Make the delegate class implement PlanItemInstanceLifecycleListener.
- Check the expression resolves to the intended bean of that type in your expression/bean context.
- If you intended a case-level listener, reconfigure the model to attach the correct listener type for plan items.
- Test delegate resolution in a unit test with a FlowableExpressionManager to fail before deployment.
Example fix
// before
public class MyHandler implements CaseInstanceLifecycleListener { ... }
// after
public class MyHandler implements PlanItemInstanceLifecycleListener {
public void stateChanged(DelegatePlanItemInstance planItemInstance, String oldState, String newState) { /* ... */ }
} Defensive patterns
Strategy: type-guard
Validate before calling
Object delegate = DelegateExpressionUtil.resolveDelegateExpression(expression, planItemInstance, null);
if (!(delegate instanceof PlanItemInstanceLifecycleListener)) {
throw new IllegalArgumentException("Expression must resolve to a PlanItemInstanceLifecycleListener");
} Type guard
boolean isPlanItemLifecycleDelegate(Object o) {
return o instanceof PlanItemInstanceLifecycleListener;
} Try / catch
try {
// plan item transition path
} catch (FlowableIllegalArgumentException e) {
if (e.getMessage() != null && e.getMessage().contains("PlanItemInstanceLifecycleListener")) {
throw new ConfigurationException("Delegate for " + expression + " must implement PlanItemInstanceLifecycleListener", e);
}
throw e;
} Prevention
- Do not reuse case-level listener beans for plan items
- Assert delegate types in a boot-time smoke test
- Validate listener element types in the CMMN XML before deploy
When it happens
Trigger: stateChanged(planItemInstance, oldState, newState) resolves the expression and the object does not pass `instanceof PlanItemInstanceLifecycleListener` — expression points at the wrong bean/property or the class implements only the case-level or task-level listener interface.
Common situations: Reusing a CaseInstanceLifecycleListener bean for a plan item listener; expression typo resolving to a service method return value; refactor changed the bean's interface; wrong listener type configured on the plan item in the CMMN XML.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Delegate expression ${expression} did not resolve to an impl
- Delegate expression ${expression} did not resolve to an impl
- Delegate expression ${expression} did not resolve to an impl
- Delegate expression ${expression} did not resolve to an impl
- Invalid usage of async_activate job handler, variable scope
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/ba7313aab6af94b4.
Report an issue: GitHub.