flowable/flowable-engine · error · ActivitiIllegalArgumentException

Delegate expression did not resolve to an implementation of…

Error message

Delegate expression ${expression} did not resolve to an implementation of interface org.activiti.engine.delegate.ExecutionListener nor interface org.activiti.engine.impl.javadelegate.JavaDelegate

What it means

Thrown as ActivitiIllegalArgumentException from DelegateExpressionExecutionListener.notify when a delegate expression resolves to an object that is neither an ExecutionListener nor a JavaDelegate. The engine needs one of these two interfaces to invoke the listener, so an arbitrary bean type is rejected.

Solutions

  1. Make the bean referenced by the expression implement org.activiti.engine.delegate.ExecutionListener (or JavaDelegate).
  2. Verify the expression resolves to the intended bean — print/log the resolved class before the listener executes.
  3. If you meant a TaskListener, use a taskListener element rather than an executionListener.
  4. Check the bean is correctly registered in the Spring context / process engine configuration beans map and not shadowed by another bean of the same name.

Example fix

// before: plain service bean
public class MyService { public void execute() { ... } }

// after: implement the listener interface
public class MyService implements ExecutionListener {
  @Override public void notify(DelegateExecution execution) { ... }
}
Defensive patterns

Strategy: type-guard

Validate before calling

Object delegate = applicationContext.getBean(beanName);
if (!(delegate instanceof ExecutionListener) && !(delegate instanceof JavaDelegate)) {
    throw new IllegalStateException(beanName + " must implement ExecutionListener or JavaDelegate");
}

Type guard

boolean isValidExecutionDelegate(Object o) {
    return o instanceof ExecutionListener || o instanceof JavaDelegate;
}

Try / catch

try {
    runtimeService.startProcessInstanceByKey("myProcess");
} catch (ActivitiIllegalArgumentException e) {
    if (e.getMessage().contains("did not resolve to an implementation")) {
        logger.error("delegateExpression bean type wrong: {}", e.getMessage());
    }
    throw e;
}

Prevention

When it happens

Trigger: A flow element declares <activiti:executionListener event="..." delegateExpression="${myBean}"/> and myBean resolves (from Spring/CDI/beans map) to an object that does not implement org.activiti.engine.delegate.ExecutionListener or org.activiti.engine.impl.javadelegate.JavaDelegate.

Common situations: Pointing the delegateExpression at a plain service bean or wrong bean (e.g. a TaskListener used for an execution listener); refactoring renamed/changed the bean's class so it no longer implements the interface; typo resolving to a different bean; returning a raw value (String/Map) from an expression instead of a listener bean.

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/6d393ccd3486f60c. Report an issue: GitHub.

Appendix: source

Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/listener/DelegateExpressionExecutionListener.java:57

    }

    @Override
    public void notify(DelegateExecution execution) {
        // Note: we can't cache the result of the expression, because the
        // execution can change: eg. delegateExpression='${mySpringBeanFactory.randomSpringBean()}'
        Object delegate = expression.getValue(execution);
        ClassDelegate.applyFieldDeclaration(fieldDeclarations, delegate);

        if (delegate instanceof ExecutionListener) {
            Context.getProcessEngineConfiguration()
                    .getDelegateInterceptor()
                    .handleInvocation(new ExecutionListenerInvocation((ExecutionListener) delegate, execution));
        } else if (delegate instanceof JavaDelegate) {
            Context.getProcessEngineConfiguration()
                    .getDelegateInterceptor()
                    .handleInvocation(new JavaDelegateInvocation((JavaDelegate) delegate, execution));
        } else {
            throw new ActivitiIllegalArgumentException("Delegate expression " + expression
                    + " did not resolve to an implementation of " + ExecutionListener.class
                    + " nor " + JavaDelegate.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)