flowable/flowable-engine · error · FlowableIllegalArgumentException
${delegateInstance.getClass().getName()} doesn't implement $
Error message
${delegateInstance.getClass().getName()} doesn't implement ${CaseInstanceLifecycleListener.class} What it means
Case-level lifecycle listeners configured with flowable:class are resolved by getCaseLifeCycleListenerInstance, which requires the class to implement CaseInstanceLifecycleListener. If the instantiated object does not, this FlowableIllegalArgumentException is thrown.
Source
Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/delegate/CmmnClassDelegate.java:146
}
protected PlanItemInstanceLifecycleListener getPlanItemLifeCycleListenerInstance() {
Object delegateInstance = instantiate(className);
applyFieldExtensions(fieldExtensions, delegateInstance, false);
if (delegateInstance instanceof PlanItemInstanceLifecycleListener) {
return (PlanItemInstanceLifecycleListener) delegateInstance;
} else {
throw new FlowableIllegalArgumentException(delegateInstance.getClass().getName() + " doesn't implement " + PlanItemInstanceLifecycleListener.class);
}
}
protected CaseInstanceLifecycleListener getCaseLifeCycleListenerInstance() {
Object delegateInstance = instantiate(className);
applyFieldExtensions(fieldExtensions, delegateInstance, false);
if (delegateInstance instanceof CaseInstanceLifecycleListener) {
return (CaseInstanceLifecycleListener) delegateInstance;
} else {
throw new FlowableIllegalArgumentException(delegateInstance.getClass().getName() + " doesn't implement " + CaseInstanceLifecycleListener.class);
}
}
@Override
public Object aggregateSingleVariable(DelegatePlanItemInstance planItemInstance, PlanItemVariableAggregatorContext context) {
return getPlanItemVariableAggregator().aggregateSingleVariable(planItemInstance, context);
}
@Override
public Object aggregateMultiVariables(DelegatePlanItemInstance planItemInstance, List<? extends VariableInstance> instances, PlanItemVariableAggregatorContext context) {
return getPlanItemVariableAggregator().aggregateMultiVariables(planItemInstance, instances, context);
}
protected PlanItemVariableAggregator getPlanItemVariableAggregator() {
Object delegateInstance = instantiate(className);
applyFieldExtensions(fieldExtensions, delegateInstance, false);
if (delegateInstance instanceof PlanItemVariableAggregator) {
return (PlanItemVariableAggregator) delegateInstance;View on GitHub (pinned to d6d39ce1c6)
Solutions
- Implement org.flowable.cmmn.api.listener.CaseInstanceLifecycleListener on the referenced class.
- If the class handles plan item state changes, reference it where a plan-item lifecycle listener is expected instead.
- Verify the fully-qualified name and that the deployed artifact contains the interface implementation.
- Align the listener's getSourceState/getTargetState implementation with the case states you want to observe.
Example fix
// before
public class CaseAuditListener implements PlanItemInstanceLifecycleListener { ... }
// after
public class CaseAuditListener implements CaseInstanceLifecycleListener {
public String getSourceState() { return null; }
public String getTargetState() { return null; }
public void stateChanged(CaseInstance caseInstance, String oldState, String newState) { ... }
} Defensive patterns
Strategy: validation
Validate before calling
Class<?> c = Class.forName(className);
if (!org.flowable.cmmn.api.listener.CaseInstanceLifecycleListener.class.isAssignableFrom(c)) {
throw new IllegalStateException(className + " must implement CaseInstanceLifecycleListener");
} Type guard
boolean isCaseLifecycleListener(Object o) { return o instanceof org.flowable.cmmn.api.listener.CaseInstanceLifecycleListener; } Try / catch
try {
caseListener.stateChanged(caseInstance, oldState, newState);
} catch (org.flowable.common.engine.api.FlowableIllegalArgumentException e) {
if (e.getMessage().contains("CaseInstanceLifecycleListener")) { log.error("Not a case lifecycle listener: {}", e.getMessage()); }
throw e;
} Prevention
- Keep case-level and plan-item-level listener classes in clearly named packages/classes.
- Validate all listener class references in case XML during CI.
- Check the error message's interface name — it names exactly what must be implemented.
When it happens
Trigger: A case definition declares a case instance lifecycle listener via flowable:class; on first use (caseLifeCycleListenerInstance, typically when a case state changes) the class is instantiated and fails the instanceof check.
Common situations: Pointing the case listener attribute at a PlanItemInstanceLifecycleListener or other delegate class; refactoring moved the interface; copy/paste of class references between plan-item and case-level listener configs.
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
- ${delegateInstance.getClass().getName()} doesn't implement $
- ${className} does not implement the ${CmmnTriggerableActivit
- ${className} does not implement the ${CmmnActivityBehavior.c
- ${delegateInstance.getClass().getName()} doesn't implement $
- ${delegateInstance.getClass().getName()} doesn't implement $
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/bbb8dc6ae15e6642.
Report an issue: GitHub.