{"record":{"id":"6d393ccd3486f60c","repo":"flowable/flowable-engine","slug":"delegate-expression-expression-did-not-resolve-6d393c","errorCode":null,"errorMessage":"Delegate expression ${expression} did not resolve to an implementation of interface org.activiti.engine.delegate.ExecutionListener nor interface org.activiti.engine.impl.javadelegate.JavaDelegate","messagePattern":"Delegate expression (.+?) did not resolve to an implementation of interface org\\.activiti\\.engine\\.delegate\\.ExecutionListener nor interface org\\.activiti\\.engine\\.impl\\.javadelegate\\.JavaDelegate","errorType":"exception","errorClass":"ActivitiIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/listener/DelegateExpressionExecutionListener.java","lineNumber":57,"sourceCode":"    }\n\n    @Override\n    public void notify(DelegateExecution execution) {\n        // Note: we can't cache the result of the expression, because the\n        // execution can change: eg. delegateExpression='${mySpringBeanFactory.randomSpringBean()}'\n        Object delegate = expression.getValue(execution);\n        ClassDelegate.applyFieldDeclaration(fieldDeclarations, delegate);\n\n        if (delegate instanceof ExecutionListener) {\n            Context.getProcessEngineConfiguration()\n                    .getDelegateInterceptor()\n                    .handleInvocation(new ExecutionListenerInvocation((ExecutionListener) delegate, execution));\n        } else if (delegate instanceof JavaDelegate) {\n            Context.getProcessEngineConfiguration()\n                    .getDelegateInterceptor()\n                    .handleInvocation(new JavaDelegateInvocation((JavaDelegate) delegate, execution));\n        } else {\n            throw new ActivitiIllegalArgumentException(\"Delegate expression \" + expression\n                    + \" did not resolve to an implementation of \" + ExecutionListener.class\n                    + \" nor \" + JavaDelegate.class);\n        }\n    }\n\n    /**\n     * returns the expression text for this execution listener. Comes in handy if you want to check which listeners you already have.\n     */\n    public String getExpressionText() {\n        return expression.getExpressionText();\n    }\n\n}\n","sourceCodeStart":39,"sourceCodeEnd":71,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/listener/DelegateExpressionExecutionListener.java#L39-L71","documentation":"Thrown as ActivitiIllegalArgumentException from DelegateExpressionExecutionListener.notify when a delegate expression resolves to an object that is neither an ExecutionListener nor a JavaDelegate. The engine needs one of these two interfaces to invoke the listener, so an arbitrary bean type is rejected.","triggerScenarios":"A flow element declares <activiti:executionListener event=\"...\" delegateExpression=\"${myBean}\"/> and myBean resolves (from Spring/CDI/beans map) to an object that does not implement org.activiti.engine.delegate.ExecutionListener or org.activiti.engine.impl.javadelegate.JavaDelegate.","commonSituations":"Pointing the delegateExpression at a plain service bean or wrong bean (e.g. a TaskListener used for an execution listener); refactoring renamed/changed the bean's class so it no longer implements the interface; typo resolving to a different bean; returning a raw value (String/Map) from an expression instead of a listener bean.","solutions":["Make the bean referenced by the expression implement org.activiti.engine.delegate.ExecutionListener (or JavaDelegate).","Verify the expression resolves to the intended bean — print/log the resolved class before the listener executes.","If you meant a TaskListener, use a taskListener element rather than an executionListener.","Check the bean is correctly registered in the Spring context / process engine configuration beans map and not shadowed by another bean of the same name."],"exampleFix":"// before: plain service bean\npublic class MyService { public void execute() { ... } }\n\n// after: implement the listener interface\npublic class MyService implements ExecutionListener {\n  @Override public void notify(DelegateExecution execution) { ... }\n}","handlingStrategy":"type-guard","validationCode":"Object delegate = applicationContext.getBean(beanName);\nif (!(delegate instanceof ExecutionListener) && !(delegate instanceof JavaDelegate)) {\n    throw new IllegalStateException(beanName + \" must implement ExecutionListener or JavaDelegate\");\n}","typeGuard":"boolean isValidExecutionDelegate(Object o) {\n    return o instanceof ExecutionListener || o instanceof JavaDelegate;\n}","tryCatchPattern":"try {\n    runtimeService.startProcessInstanceByKey(\"myProcess\");\n} catch (ActivitiIllegalArgumentException e) {\n    if (e.getMessage().contains(\"did not resolve to an implementation\")) {\n        logger.error(\"delegateExpression bean type wrong: {}\", e.getMessage());\n    }\n    throw e;\n}","preventionTips":["Have delegate beans implement the exact interface required by the listener element type.","Use distinct beans for ExecutionListener, TaskListener, and JavaDelegate roles.","Add a startup smoke test that resolves every delegateExpression bean and asserts its type.","Log resolved bean classes when wiring delegate expressions."],"tags":["bpmn","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"}