flowable/flowable-engine · error · FlowableIllegalArgumentException

Delegate expression " + expression + " did not resolve to an

Error message

Delegate expression " + expression + " did not resolve to an implementation of " + TransactionDependentTaskListener.class

What it means

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.

Source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/listener/DelegateExpressionTransactionDependentTaskListener.java:43

 */
public class DelegateExpressionTransactionDependentTaskListener implements TransactionDependentTaskListener {

    protected Expression expression;

    public DelegateExpressionTransactionDependentTaskListener(Expression expression) {
        this.expression = expression;
    }

    @Override
    public void notify(String processInstanceId, String executionId, Task task, Map<String, Object> executionVariables, Map<String, Object> customPropertiesMap) {
        NoExecutionVariableScope scope = new NoExecutionVariableScope();

        Object delegate = expression.getValue(scope);

        if (delegate instanceof TransactionDependentTaskListener) {
            ((TransactionDependentTaskListener) delegate).notify(processInstanceId, executionId, task, executionVariables, customPropertiesMap);
        } else {
            throw new FlowableIllegalArgumentException("Delegate expression " + expression + " did not resolve to an implementation of " + TransactionDependentTaskListener.class);
        }

    }

    /**
     * returns the expression text for this task listener. Comes in handy if you want to check which listeners you already have.
     */
    public String getExpressionText() {
        return expression.getExpressionText();
    }

}

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Implement org.flowable.engine.delegate.TransactionDependentTaskListener with notify(processInstanceId, executionId, task, executionVariables, customPropertiesMap).
  2. Point the delegateExpression at a bean that genuinely implements TransactionDependentTaskListener.
  3. If the listener is a regular TaskListener, configure it as a normal task listener rather than a transaction-dependent one.
  4. For cross-listener reuse, extract shared logic into a helper called by both a TaskListener and a TransactionDependentTaskListener wrapper.

Example fix

// before
public class TaskAudit implements TaskListener { ... } // wrong interface for tx-dependent config
// after
public class TaskAudit implements TransactionDependentTaskListener {
  @Override
  public void notify(String processInstanceId, String executionId, DelegateTask task,
      Map<String, Object> executionVariables, Map<String, Object> customPropertiesMap) { ... }
}
Defensive patterns

Strategy: type-guard

Validate before calling

Object bean = applicationContext.getBean("taskAuditListener");
if (!(bean instanceof org.flowable.engine.delegate.TransactionDependentTaskListener)) {
    throw new IllegalStateException("taskAuditListener must implement TransactionDependentTaskListener");
}

Type guard

public static boolean isTransactionDependentTaskListener(Object o) {
    return o instanceof org.flowable.engine.delegate.TransactionDependentTaskListener;
}

Try / catch

try {
    listener.notify(processInstanceId, executionId, task, vars, customProps);
} catch (org.flowable.common.engine.api.FlowableIllegalArgumentException e) {
    log.error("Transaction-dependent task listener delegate is wrong type: {}", e.getMessage());
    throw new ConfigurationException("delegateExpression must resolve to a TransactionDependentTaskListener", e);
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/f22d5843af7a4706. Report an issue: GitHub.