flowable/flowable-engine · error · FlowableIllegalArgumentException

doesn't implement TaskListener

Error message

<delegateClassName> doesn't implement TaskListener

What it means

ClassDelegate instantiates the class configured as a task listener and verifies it implements TaskListener before delegating notify() to it. If the object does not implement org.flowable.task.service.delegate.TaskListener, a FlowableIllegalArgumentException naming the offending class is thrown. This guards the engine contract that task listeners expose the notify(DelegateTask) hook.

Solutions

  1. Make the class implement org.flowable.task.service.delegate.TaskListener (notify(DelegateTask)).
  2. Confirm the import is the Flowable TaskListener, not the Activiti or old Flowable 5 one.
  3. Verify the class attribute in the BPMN XML names the correct class.
  4. Rebuild and redeploy the process application.

Example fix

// before
import org.activiti.api.task.runtime.events.listener.TaskEventListener;
public class MyTaskListener implements TaskEventListener { ... }
// after
import org.flowable.task.service.delegate.TaskListener;
public class MyTaskListener implements TaskListener {
    @Override
    public void notify(DelegateTask delegateTask) { ... }
}
Defensive patterns

Strategy: validation

Validate before calling

Class<?> c = Class.forName(taskListenerClass);
if (!org.flowable.task.service.delegate.TaskListener.class.isAssignableFrom(c))
    throw new IllegalStateException("Must implement Flowable TaskListener");

Type guard

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

Try / catch

try {
    taskService.complete(taskId);
} catch (FlowableIllegalArgumentException e) {
    if (e.getMessage().contains("TaskListener")) { /* fix listener class */ }
    throw e;
}

Prevention

When it happens

Trigger: A user task declares flowable:taskListener with class='<delegateClassName>' and the class does not implement TaskListener; the check fires both from taskListenerInstance() and during notify().

Common situations: Pointing a task listener at an ExecutionListener or JavaDelegate class; using the old org.activiti TaskListener import instead of the Flowable one; refactoring removed the interface; wrong fully qualified name in XML.

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

Appendix: source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/ClassDelegate.java:165

            throw new FlowableIllegalArgumentException(delegateInstance.getClass().getName() + " doesn't implement " + TransactionDependentExecutionListener.class);
        }
    }

    protected CustomPropertiesResolver getCustomPropertiesResolverInstance() {
        Object delegateInstance = instantiateDelegate(className, fieldDeclarations);
        if (delegateInstance instanceof CustomPropertiesResolver) {
            return (CustomPropertiesResolver) delegateInstance;
        } else {
            throw new FlowableIllegalArgumentException(delegateInstance.getClass().getName() + " doesn't implement " + CustomPropertiesResolver.class);
        }
    }

    protected TaskListener getTaskListenerInstance() {
        Object delegateInstance = instantiateDelegate(className, fieldDeclarations);
        if (delegateInstance instanceof TaskListener) {
            return (TaskListener) delegateInstance;
        } else {
            throw new FlowableIllegalArgumentException(delegateInstance.getClass().getName() + " doesn't implement " + TaskListener.class);
        }
    }

    protected TransactionDependentTaskListener getTransactionDependentTaskListenerInstance() {
        Object delegateInstance = instantiateDelegate(className, fieldDeclarations);
        if (delegateInstance instanceof TransactionDependentTaskListener) {
            return (TransactionDependentTaskListener) delegateInstance;
        } else {
            throw new FlowableIllegalArgumentException(delegateInstance.getClass().getName() + " doesn't implement " + TransactionDependentTaskListener.class);
        }
    }

    // Activity Behavior
    @Override
    public void execute(DelegateExecution execution) {
        if (CommandContextUtil.getProcessEngineConfiguration().isEnableProcessDefinitionInfoCache()) {
            ObjectNode taskElementProperties = BpmnOverrideContext.getBpmnOverrideElementProperties(serviceTaskId, execution.getProcessDefinitionId());
            if (taskElementProperties != null && taskElementProperties.has(DynamicBpmnConstants.SERVICE_TASK_CLASS_NAME)) {

View on GitHub (pinned to d6d39ce1c6)