{"record":{"id":"f22d5843af7a4706","repo":"flowable/flowable-engine","slug":"delegate-expression-expression-did-not-res-f22d58","errorCode":null,"errorMessage":"Delegate expression \" + expression + \" did not resolve to an implementation of \" + TransactionDependentTaskListener.class","messagePattern":"Delegate expression \" \\+ expression \\+ \" did not resolve to an implementation of \" \\+ TransactionDependentTaskListener\\.class","errorType":"exception","errorClass":"FlowableIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/listener/DelegateExpressionTransactionDependentTaskListener.java","lineNumber":43,"sourceCode":" */\npublic class DelegateExpressionTransactionDependentTaskListener implements TransactionDependentTaskListener {\n\n    protected Expression expression;\n\n    public DelegateExpressionTransactionDependentTaskListener(Expression expression) {\n        this.expression = expression;\n    }\n\n    @Override\n    public void notify(String processInstanceId, String executionId, Task task, Map<String, Object> executionVariables, Map<String, Object> customPropertiesMap) {\n        NoExecutionVariableScope scope = new NoExecutionVariableScope();\n\n        Object delegate = expression.getValue(scope);\n\n        if (delegate instanceof TransactionDependentTaskListener) {\n            ((TransactionDependentTaskListener) delegate).notify(processInstanceId, executionId, task, executionVariables, customPropertiesMap);\n        } else {\n            throw new FlowableIllegalArgumentException(\"Delegate expression \" + expression + \" did not resolve to an implementation of \" + TransactionDependentTaskListener.class);\n        }\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":25,"sourceCodeEnd":56,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/listener/DelegateExpressionTransactionDependentTaskListener.java#L25-L56","documentation":"DelegateExpressionTransactionDependentTaskListener evaluates its delegate expression when a transaction-dependent task event is dispatched. The resolved object must implement TransactionDependentTaskListener; otherwise notify throws FlowableIllegalArgumentException. Like its execution counterpart, this fires after the transaction commits, so the misconfiguration is detected at dispatch time.","triggerScenarios":"A user task uses a transaction-dependent task listener with delegateExpression='${someBean}'; the expression resolves to a bean that does not implement org.flowable.engine.delegate.TransactionDependentTaskListener.","commonSituations":"Bean implements plain TaskListener or TransactionDependentExecutionListener instead of the TransactionDependentTaskListener variant; copy-paste of listener config between execution and task elements; migration renamed interfaces so the bean implements the wrong one.","solutions":["Implement org.flowable.engine.delegate.TransactionDependentTaskListener with notify(processInstanceId, executionId, task, executionVariables, customPropertiesMap).","Point the delegateExpression at a bean that genuinely implements TransactionDependentTaskListener.","If the listener is a regular TaskListener, configure it as a normal task listener rather than a transaction-dependent one.","For cross-listener reuse, extract shared logic into a helper called by both a TaskListener and a TransactionDependentTaskListener wrapper."],"exampleFix":"// before\npublic class TaskAudit implements TaskListener { ... } // wrong interface for tx-dependent config\n// after\npublic class TaskAudit implements TransactionDependentTaskListener {\n  @Override\n  public void notify(String processInstanceId, String executionId, DelegateTask task,\n      Map<String, Object> executionVariables, Map<String, Object> customPropertiesMap) { ... }\n}","handlingStrategy":"type-guard","validationCode":"Object bean = applicationContext.getBean(\"taskAuditListener\");\nif (!(bean instanceof org.flowable.engine.delegate.TransactionDependentTaskListener)) {\n    throw new IllegalStateException(\"taskAuditListener must implement TransactionDependentTaskListener\");\n}","typeGuard":"public static boolean isTransactionDependentTaskListener(Object o) {\n    return o instanceof org.flowable.engine.delegate.TransactionDependentTaskListener;\n}","tryCatchPattern":"try {\n    listener.notify(processInstanceId, executionId, task, vars, customProps);\n} catch (org.flowable.common.engine.api.FlowableIllegalArgumentException e) {\n    log.error(\"Transaction-dependent task listener delegate is wrong type: {}\", e.getMessage());\n    throw new ConfigurationException(\"delegateExpression must resolve to a TransactionDependentTaskListener\", e);\n}","preventionTips":["Use distinct bean names for task vs execution transaction-dependent listeners to avoid copy-paste mis-wiring.","Assert the implemented interface in a unit test for every listener bean.","Grep BPMN XML for transaction-dependent task listeners and cross-check each against the bean registry at deploy time.","After refactors, re-verify that implements clauses still reference org.flowable.engine.delegate types."],"tags":["flowable","bpmn","delegate-expression","transaction-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"}