{"record":{"id":"c5da7d223ebeaba8","repo":"flowable/flowable-engine","slug":"delegate-expression-expression-did-not-resolve-c5da7d","errorCode":null,"errorMessage":"Delegate expression ${expression} did not resolve to an implementation of interface org.activiti.engine.delegate.TaskListener","messagePattern":"Delegate expression (.+?) did not resolve to an implementation of interface org\\.activiti\\.engine\\.delegate\\.TaskListener","errorType":"exception","errorClass":"ActivitiIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/listener/DelegateExpressionTaskListener.java","lineNumber":56,"sourceCode":"    }\n\n    @Override\n    public void notify(DelegateTask delegateTask) {\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(delegateTask);\n        ClassDelegate.applyFieldDeclaration(fieldDeclarations, delegate);\n\n        if (delegate instanceof TaskListener) {\n            try {\n                Context.getProcessEngineConfiguration()\n                        .getDelegateInterceptor()\n                        .handleInvocation(new TaskListenerInvocation((TaskListener) delegate, delegateTask));\n            } catch (Exception e) {\n                throw new ActivitiException(\"Exception while invoking TaskListener: \" + e.getMessage(), e);\n            }\n        } else {\n            throw new ActivitiIllegalArgumentException(\"Delegate expression \" + expression\n                    + \" did not resolve to an implementation of \" + TaskListener.class);\n        }\n    }\n\n    /**\n     * returns the expression text for this task 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":38,"sourceCodeEnd":69,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/listener/DelegateExpressionTaskListener.java#L38-L69","documentation":"Thrown as ActivitiIllegalArgumentException from DelegateExpressionTaskListener.notify when a delegate expression resolves to an object that is not an implementation of org.activiti.engine.delegate.TaskListener. The engine can only invoke TaskListener instances for task listeners, so any other type is rejected.","triggerScenarios":"A <activiti:taskListener event=\"...\" delegateExpression=\"${bean}\"/> where the expression resolves to an ExecutionListener, a JavaDelegate, a plain bean, or any non-TaskListener object.","commonSituations":"Reusing a bean that implements ExecutionListener/JavaDelegate as a task listener; refactoring removed the TaskListener interface from the class; expression typo resolving to the wrong bean; bean returns a proxy class that lost the interface (e.g. misconfigured proxying).","solutions":["Make the referenced bean implement org.activiti.engine.delegate.TaskListener and its notify(DelegateTask) method.","Verify the expression points at the intended bean and that the bean's class still implements the interface.","If the bean is actually an ExecutionListener, declare it as an executionListener element instead.","Check for proxy/serialization issues (e.g. wrong proxy target class) that strip the TaskListener interface at runtime."],"exampleFix":"// before: wrong interface\npublic class MyListener implements ExecutionListener { ... }\n\n// after: correct interface for a task listener\npublic class MyListener implements TaskListener {\n  @Override public void notify(DelegateTask delegateTask) { ... }\n}","handlingStrategy":"type-guard","validationCode":"Object delegate = applicationContext.getBean(beanName);\nif (!(delegate instanceof TaskListener)) {\n    throw new IllegalStateException(beanName + \" must implement org.activiti.engine.delegate.TaskListener\");\n}","typeGuard":"boolean isValidTaskDelegate(Object o) {\n    return o instanceof TaskListener;\n}","tryCatchPattern":"try {\n    taskService.complete(taskId);\n} catch (ActivitiIllegalArgumentException e) {\n    if (e.getMessage().contains(\"did not resolve to an implementation of\")) {\n        logger.error(\"taskListener delegateExpression must resolve to a TaskListener: {}\", e.getMessage());\n    }\n    throw e;\n}","preventionTips":["Implement org.activiti.engine.delegate.TaskListener (not ExecutionListener) for taskListener elements.","Assert bean types at application startup for every delegateExpression reference.","Keep one bean per listener role to avoid accidental reuse.","Beware proxies/serialization that may strip interfaces — test resolution at runtime."],"tags":["bpmn","task-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-18T11:17:12.947Z"}