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

  1. Make the delegate class implement PlanItemInstanceLifecycleListener.
  2. Check the expression resolves to the intended bean of that type in your expression/bean context.
  3. If you intended a case-level listener, reconfigure the model to attach the correct listener type for plan items.
  4. 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

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


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/ba7313aab6af94b4. Report an issue: GitHub.