{"record":{"id":"cb890d32efcf256f","repo":"flowable/flowable-engine","slug":"delegate-expression-expression-did-not-resolve-cb890d","errorCode":null,"errorMessage":"Delegate expression ${expression} did not resolve to an implementation of ${CaseInstanceLifecycleListener.class}","messagePattern":"Delegate expression (.+?) did not resolve to an implementation of (.+?)","errorType":"exception","errorClass":"FlowableIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/listener/DelegateExpressionCaseLifecycleListener.java","lineNumber":61,"sourceCode":"    @Override\n    public String getSourceState() {\n        return sourceState;\n    }\n\n    @Override\n    public String getTargetState() {\n        return targetState;\n    }\n\n    @Override\n    public void stateChanged(CaseInstance caseInstance, String oldState, String newState) {\n        CaseInstanceEntity caseInstanceEntity = (CaseInstanceEntity) caseInstance;\n        Object delegate = DelegateExpressionUtil.resolveDelegateExpression(expression, caseInstanceEntity, fieldExtensions);\n\n        if (delegate instanceof CaseInstanceLifecycleListener listener) {\n            listener.stateChanged(caseInstanceEntity, oldState, newState);\n        } else {\n            throw new FlowableIllegalArgumentException(\"Delegate expression \" + expression + \" did not resolve to an implementation of \" + CaseInstanceLifecycleListener.class);\n        }\n    }\n\n    /**\n     * returns the expression text for this CaseInstance lifecycle listener.\n     */\n    public String getExpressionText() {\n        return expression.getExpressionText();\n    }\n\n}\n","sourceCodeStart":43,"sourceCodeEnd":73,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/listener/DelegateExpressionCaseLifecycleListener.java#L43-L73","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Make the class behind the expression implement CaseInstanceLifecycleListener (and its stateChanged method).","Verify the expression string resolves to the intended bean (check bean name/type in your Spring/CDI context).","If the delegate is intentionally not a listener, remove the listener definition from the case model.","Add a deploy-time check/test that instantiates the delegate to fail fast on type mismatches."],"exampleFix":"// before\npublic class MyCaseHandler { public void onChange(CaseInstance c) {} }\n// after\npublic class MyCaseHandler implements CaseInstanceLifecycleListener {\n    public void stateChanged(CaseInstance caseInstance, String oldState, String newState) { /* ... */ }\n}","handlingStrategy":"type-guard","validationCode":"Object delegate = DelegateExpressionUtil.resolveDelegateExpression(expression, caseInstance, null);\nif (!(delegate instanceof CaseInstanceLifecycleListener)) {\n    throw new IllegalArgumentException(\"Expression must resolve to a CaseInstanceLifecycleListener\");\n}","typeGuard":"boolean isCaseLifecycleDelegate(Object o) {\n    return o instanceof CaseInstanceLifecycleListener;\n}","tryCatchPattern":"try {\n    // case state change path\n} catch (FlowableIllegalArgumentException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"CaseInstanceLifecycleListener\")) {\n        throw new ConfigurationException(\"Delegate bean for \" + expression + \" must implement CaseInstanceLifecycleListener\", e);\n    }\n    throw e;\n}","preventionTips":["Write a unit test resolving every delegate expression and asserting the interface","Keep bean names and types in sync after refactors","Use distinct bean names per listener role (case vs plan item vs task)"],"tags":["flowable","cmmn","lifecycle-listener","delegate-expression","type-mismatch"],"backgroundTag":"type-mismatch","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}