flowable/flowable-engine · error · FlowableIllegalArgumentException

Delegate expression ${expression} did not resolve to an impl

Error message

Delegate expression ${expression} did not resolve to an implementation of ${TaskListener.class}

What it means

DelegateExpressionTaskListener resolves a delegate expression against a DelegateTask and expects the result to implement TaskListener. When the resolved object is not a TaskListener it throws FlowableIllegalArgumentException. Same guard family as the case/plan-item delegate listeners, applied to task listeners.

Source

Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/listener/DelegateExpressionTaskListener.java:44

 */
public class DelegateExpressionTaskListener implements TaskListener {

    protected Expression expression;
    protected List<FieldExtension> fieldExtensions;

    public DelegateExpressionTaskListener(Expression expression, List<FieldExtension> fieldExtensions) {
        this.expression = expression;
        this.fieldExtensions = fieldExtensions;
    }

    @Override
    public void notify(DelegateTask delegateTask) {
        Object delegate = DelegateExpressionUtil.resolveDelegateExpression(expression, delegateTask, fieldExtensions);

        if (delegate instanceof TaskListener taskListener) {
            taskListener.notify(delegateTask);
        } else {
            throw new FlowableIllegalArgumentException("Delegate expression " + expression + " did not resolve to an implementation of " + TaskListener.class);
        }
    }

    /**
     * returns the expression text for this task listener.
     */
    public String getExpressionText() {
        return expression.getExpressionText();
    }

}

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Make the delegate implement org.flowable.task.service.delegate.TaskListener (the CMMN one).
  2. Verify you are not mixing the BPMN engine's TaskListener interface with the CMMN one — a very common cause.
  3. Fix the expression to reference the correct bean name/type.
  4. Add a unit test resolving each delegate expression and asserting the expected interface.

Example fix

// before
public class MyListener implements org.flowable.engine.delegate.TaskListener { ... } // BPMN interface
// after
public class MyListener implements org.flowable.task.service.delegate.TaskListener {
    public void notify(DelegateTask delegateTask) { /* ... */ }
}
Defensive patterns

Strategy: type-guard

Validate before calling

Object delegate = DelegateExpressionUtil.resolveDelegateExpression(expression, delegateTask, null);
if (!(delegate instanceof org.flowable.task.service.delegate.TaskListener)) {
    throw new IllegalArgumentException("Expression must resolve to a CMMN TaskListener");
}

Type guard

boolean isCmmnTaskListener(Object o) {
    return o instanceof org.flowable.task.service.delegate.TaskListener;
}

Try / catch

try {
    // task creation triggers listeners
} catch (FlowableIllegalArgumentException e) {
    if (e.getMessage() != null && e.getMessage().contains("TaskListener")) {
        throw new ConfigurationException("Delegate for " + expression + " must implement the CMMN TaskListener", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: notify(delegateTask) resolves the expression and the object fails `instanceof TaskListener` — the expression points at a bean or value that is not a task listener.

Common situations: Spring bean exposed under the referenced name has the wrong type; a BPMN TaskListener (org.flowable.engine.delegate.TaskListener) is referenced where the CMMN TaskListener is required; expression typo returning a scalar.

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