{"record":{"id":"26da40c92fd2641a","repo":"flowable/flowable-engine","slug":"delegate-expression-expression-did-not-res-26da40","errorCode":null,"errorMessage":"Delegate expression \" + expression + \" did not resolve to an implementation of \" + ExecutionListener.class + \" nor \" + JavaDelegate.class","messagePattern":"Delegate expression \" \\+ expression \\+ \" did not resolve to an implementation of \" \\+ ExecutionListener\\.class \\+ \" nor \" \\+ JavaDelegate\\.class","errorType":"exception","errorClass":"FlowableIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/listener/DelegateExpressionExecutionListener.java","lineNumber":49,"sourceCode":"public class DelegateExpressionExecutionListener implements ExecutionListener {\n\n    protected Expression expression;\n    private final List<FieldDeclaration> fieldDeclarations;\n\n    public DelegateExpressionExecutionListener(Expression expression, List<FieldDeclaration> fieldDeclarations) {\n        this.expression = expression;\n        this.fieldDeclarations = fieldDeclarations;\n    }\n\n    @Override\n    public void notify(DelegateExecution execution) {\n        Object delegate = DelegateExpressionUtil.resolveDelegateExpression(expression, execution, fieldDeclarations);\n        if (delegate instanceof ExecutionListener) {\n            CommandContextUtil.getProcessEngineConfiguration().getDelegateInterceptor().handleInvocation(new ExecutionListenerInvocation((ExecutionListener) delegate, execution));\n        } else if (delegate instanceof JavaDelegate) {\n            CommandContextUtil.getProcessEngineConfiguration().getDelegateInterceptor().handleInvocation(new JavaDelegateInvocation((JavaDelegate) delegate, execution));\n        } else {\n            throw new FlowableIllegalArgumentException(\"Delegate expression \" + expression + \" did not resolve to an implementation of \" + ExecutionListener.class + \" 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":31,"sourceCodeEnd":61,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/listener/DelegateExpressionExecutionListener.java#L31-L61","documentation":"DelegateExpressionExecutionListener.resolveDelegateExpression resolves the configured delegate expression at event time. The result must implement either ExecutionListener or JavaDelegate (JavaDelegate instances are wrapped in JavaDelegateInvocation); otherwise notify throws FlowableIllegalArgumentException. Flowable deliberately supports both types here, so any other object type fails.","triggerScenarios":"An execution listener is declared with flowable:delegateExpression='${someBean}' on a process/flow element; the expression resolves to a bean that implements neither org.flowable.engine.delegate.ExecutionListener nor org.flowable.engine.delegate.JavaDelegate.","commonSituations":"Bean implements the old Activiti ExecutionListener interface after a migration; bean implements TaskListener instead of ExecutionListener (wrong listener type on element); expression typo resolving to an unrelated bean; expression calling a method that returns a domain object rather than a listener.","solutions":["Implement org.flowable.engine.delegate.ExecutionListener (notify(DelegateExecution)) in the delegate bean.","If the bean is meant to do service work, implement org.flowable.engine.delegate.JavaDelegate (execute(DelegateExecution)) instead — this listener also accepts JavaDelegates.","Fix the delegate expression to point at the bean that actually implements one of the accepted interfaces.","After an Activiti-to-Flowable migration, update imports from org.activiti.* delegate interfaces to org.flowable.engine.delegate.*."],"exampleFix":"// before\n<flowable:executionListener event=\"start\" delegateExpression=\"${orderHelper}\" />\n// orderHelper implements neither interface\n// after: make OrderHelper implement the interface\npublic class OrderHelper implements ExecutionListener {\n  @Override\n  public void notify(DelegateExecution execution) { ... }\n}","handlingStrategy":"validation","validationCode":"Object bean = applicationContext.getBean(\"orderHelper\");\nif (!(bean instanceof ExecutionListener) && !(bean instanceof JavaDelegate)) {\n    throw new IllegalStateException(\"orderHelper must implement ExecutionListener or JavaDelegate\");\n}","typeGuard":"public static boolean isValidExecutionDelegate(Object o) {\n    return o instanceof org.flowable.engine.delegate.ExecutionListener\n        || o instanceof org.flowable.engine.delegate.JavaDelegate;\n}","tryCatchPattern":"try {\n    executionListener.notify(execution);\n} catch (org.flowable.common.engine.api.FlowableIllegalArgumentException e) {\n    log.error(\"Bad delegateExpression config: {}\", e.getMessage());\n    throw new IllegalArgumentException(\"delegateExpression must resolve to an ExecutionListener or JavaDelegate\", e);\n}","preventionTips":["Run a startup validation pass that resolves all delegateExpression values in deployed definitions and asserts their types.","Never reuse a TaskListener bean in an execution-listener slot; name beans by role (e.g. 'orderExecutionListener').","After an Activiti migration, grep for org.activiti imports in delegate classes.","Add tests that execute each listener path with a mocked DelegateExecution."],"tags":["flowable","bpmn","delegate-expression","execution-listener","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"}