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 " + TransactionDependentExecutionListener.class

What it means

DelegateExpressionTransactionDependentExecutionListener evaluates its delegate expression when the transaction-dependent execution event is dispatched. The resolved object must implement TransactionDependentExecutionListener; otherwise notify throws FlowableIllegalArgumentException. Transaction-dependent listeners run after commit, so this error surfaces deferred from the point where the listener was registered.

Source

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

 */
public class DelegateExpressionTransactionDependentExecutionListener implements TransactionDependentExecutionListener {

    protected Expression expression;

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

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

        Object delegate = expression.getValue(scope);

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

    }

    /**
     * returns the expression text for this execution 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.TransactionDependentExecutionListener with the full notify(processInstanceId, executionId, flowElement, executionVariables, customPropertiesMap) signature.
  2. Verify the delegate expression resolves to the transaction-dependent listener bean, not a regular ExecutionListener bean.
  3. If a plain ExecutionListener is intended, configure it as a normal (immediate) execution listener instead of a transaction-dependent one.
  4. Re-check the bean after refactors to ensure the interface was not accidentally removed.

Example fix

// before
public class AfterCommitListener implements ExecutionListener { ... } // wrong interface
// after
public class AfterCommitListener implements TransactionDependentExecutionListener {
  @Override
  public void notify(String processInstanceId, String executionId, FlowElement flowElement,
      Map<String, Object> executionVariables, Map<String, Object> customPropertiesMap) { ... }
}
Defensive patterns

Strategy: type-guard

Validate before calling

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

Type guard

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

Try / catch

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

Prevention

When it happens

Trigger: An element uses flowable:executionListener with delegateExpression in a transaction-dependent (after-commit) context; the expression resolves to a bean that does not implement org.flowable.engine.delegate.TransactionDependentExecutionListener.

Common situations: Developer implements the plain ExecutionListener interface instead of the TransactionDependentExecutionListener variant (which requires notify(processInstanceId, executionId, flowElement, executionVariables, customPropertiesMap)); copy-paste from a regular listener config; bean refactoring dropped the interface.

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/2f64b64ab475f9c8. Report an issue: GitHub.