Activiti/Activiti · error · ActivitiIllegalArgumentException

Delegate expression did not resolve to an implementation of…

Error message

Delegate expression ${expression} did not resolve to an implementation of class org.activiti.engine.delegate.TransactionDependentExecutionListener

What it means

Thrown when a delegateExpression for a transaction-dependent execution listener resolves to an object that does not implement TransactionDependentExecutionListener. Transaction-dependent listeners run only on successful transaction commit, so Activiti validates the resolved type before invoking.

Solutions

  1. Implement org.activiti.engine.delegate.TransactionDependentExecutionListener (notify(DelegateExecution, FlowElement, Map, Map)) on the target bean
  2. Use a standard ExecutionListener instead if commit-time semantics are not required
  3. Confirm the expression resolves to the intended bean at runtime

Example fix

// before
public class MyBean implements ExecutionListener {...}
// used in transaction-dependent listener

// after
public class MyBean implements TransactionDependentExecutionListener {
  @Override public void notify(DelegateExecution e, FlowElement fe, Map<String,Object> vars, Map<String,Object> props) {...}
}
Defensive patterns

Strategy: type-guard

Validate before calling

Object bean = applicationContext.getBean(beanName);
if (!(bean instanceof org.activiti.engine.delegate.TransactionDependentExecutionListener))
  throw new IllegalStateException(beanName + " must implement TransactionDependentExecutionListener");

Type guard

boolean isTxExecListener(Object o) { return o instanceof org.activiti.engine.delegate.TransactionDependentExecutionListener; }

Try / catch

try {
  runtimeService.startProcessInstanceByKey(key);
} catch (ActivitiIllegalArgumentException e) {
  if (e.getMessage().contains("TransactionDependentExecutionListener")) {
    logger.error("Delegate bean implements wrong interface", e);
  }
}

Prevention

When it happens

Trigger: BPMN <activiti:executionListener event="end"> (transaction-dependent variant) with delegateExpression="${bean}" where bean is not a TransactionDependentExecutionListener.

Common situations: Reusing a plain ExecutionListener or JavaDelegate bean in a transaction-dependent listener slot; refactoring removed the interface; wrong bean name.

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 Activiti/Activiti@56435b1a97 (2026-09-09). Data as JSON: /api/errors/34a4f30c56187890. Report an issue: GitHub.

Appendix: source

Thrown at activiti-core/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/listener/DelegateExpressionTransactionDependentExecutionListener.java:57

        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 ActivitiIllegalArgumentException(
                "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 56435b1a97)