flowable/flowable-engine · error · FlowableIllegalArgumentException

<delegateClassName> doesn't implement TransactionDependentTa

Error message

<delegateClassName> doesn't implement TransactionDependentTaskListener

What it means

ClassDelegate instantiates the class configured as a transaction-dependent task listener and casts it to TransactionDependentTaskListener. If the instance does not implement that interface, a FlowableIllegalArgumentException naming the class and expected interface is thrown. Flowable needs this interface to invoke listeners during the transaction commit/rollback phases of a task.

Source

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

            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)) {
                String overrideClassName = taskElementProperties.get(DynamicBpmnConstants.SERVICE_TASK_CLASS_NAME).asString();
                if (StringUtils.isNotEmpty(overrideClassName) && !overrideClassName.equals(className)) {
                    className = overrideClassName;
                    activityBehaviorInstance = null;
                }
            }
        }

        if (activityBehaviorInstance == null) {

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Make the configured class implement TransactionDependentTaskListener (getListenerEvents + notify).
  2. Check the XML class attribute matches the intended fully qualified class.
  3. Redeploy so the deployed artifact contains the implementation.
  4. Use a regular flowable:taskListener if transaction-phase notification is not required.

Example fix

// before
public class MyListener implements TaskListener { ... }
<!-- flowable:transactionDependentTaskListener class="com.acme.MyListener" -->
// after
public class MyListener implements TransactionDependentTaskListener {
    @Override public Set<String> getListenerEvents() { return Collections.singleton(EVENTNAME_COMMITTED); }
    @Override public void notify(TransactionDependentTaskListenerExecution execution, List<TaskInfo> tasks) { ... }
}
Defensive patterns

Strategy: validation

Validate before calling

Class<?> c = Class.forName(listenerClass);
if (!TransactionDependentTaskListener.class.isAssignableFrom(c))
    throw new IllegalStateException("Must implement TransactionDependentTaskListener");

Type guard

boolean isValid(Object o) { return o instanceof TransactionDependentTaskListener; }

Try / catch

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

Prevention

When it happens

Trigger: A user task declares flowable:transactionDependentTaskListener class='<delegateClassName>' and the class does not implement org.flowable.engine.delegate.TransactionDependentTaskListener.

Common situations: Reusing a plain TaskListener class under the transactionDependent variant; refactoring dropped the interface; wrong class name in the BPMN XML; stale deployed jar missing the implementation.

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