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 ${CaseInstanceLifecycleListener.class}

What it means

DelegateExpressionCaseLifecycleListener resolves a delegate expression against the case instance and expects the result to implement CaseInstanceLifecycleListener. If the resolved object is of some other type, the listener throws FlowableIllegalArgumentException naming the expected interface. This guards against expressions pointing at beans that are not valid lifecycle listeners.

Source

Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/listener/DelegateExpressionCaseLifecycleListener.java:61

    @Override
    public String getSourceState() {
        return sourceState;
    }

    @Override
    public String getTargetState() {
        return targetState;
    }

    @Override
    public void stateChanged(CaseInstance caseInstance, String oldState, String newState) {
        CaseInstanceEntity caseInstanceEntity = (CaseInstanceEntity) caseInstance;
        Object delegate = DelegateExpressionUtil.resolveDelegateExpression(expression, caseInstanceEntity, fieldExtensions);

        if (delegate instanceof CaseInstanceLifecycleListener listener) {
            listener.stateChanged(caseInstanceEntity, oldState, newState);
        } else {
            throw new FlowableIllegalArgumentException("Delegate expression " + expression + " did not resolve to an implementation of " + CaseInstanceLifecycleListener.class);
        }
    }

    /**
     * returns the expression text for this CaseInstance lifecycle listener.
     */
    public String getExpressionText() {
        return expression.getExpressionText();
    }

}

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Make the class behind the expression implement CaseInstanceLifecycleListener (and its stateChanged method).
  2. Verify the expression string resolves to the intended bean (check bean name/type in your Spring/CDI context).
  3. If the delegate is intentionally not a listener, remove the listener definition from the case model.
  4. Add a deploy-time check/test that instantiates the delegate to fail fast on type mismatches.

Example fix

// before
public class MyCaseHandler { public void onChange(CaseInstance c) {} }
// after
public class MyCaseHandler implements CaseInstanceLifecycleListener {
    public void stateChanged(CaseInstance caseInstance, String oldState, String newState) { /* ... */ }
}
Defensive patterns

Strategy: type-guard

Validate before calling

Object delegate = DelegateExpressionUtil.resolveDelegateExpression(expression, caseInstance, null);
if (!(delegate instanceof CaseInstanceLifecycleListener)) {
    throw new IllegalArgumentException("Expression must resolve to a CaseInstanceLifecycleListener");
}

Type guard

boolean isCaseLifecycleDelegate(Object o) {
    return o instanceof CaseInstanceLifecycleListener;
}

Try / catch

try {
    // case state change path
} catch (FlowableIllegalArgumentException e) {
    if (e.getMessage() != null && e.getMessage().contains("CaseInstanceLifecycleListener")) {
        throw new ConfigurationException("Delegate bean for " + expression + " must implement CaseInstanceLifecycleListener", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: stateChanged(caseInstance, oldState, newState) evaluates the expression and obtains a non-null object that fails the `instanceof CaseInstanceLifecycleListener` check (e.g. a Map, String, or arbitrary bean).

Common situations: Expression bound to a Spring bean of the wrong class; a bean renamed/refactored so the expression resolves to something else; typo resolving to a property value instead of a bean; expression returning null handled separately, but returning the wrong type is common after refactors.

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/cb890d32efcf256f. Report an issue: GitHub.