flowable/flowable-engine · error · FlowableIllegalArgumentException

<delegateClassName> doesn't implement TransactionDependentEx

Error message

<delegateClassName> doesn't implement TransactionDependentExecutionListener

What it means

Flowable's ClassDelegate instantiates the class configured for a transaction-dependent execution listener and checks that it implements TransactionDependentExecutionListener. If the instantiated object does not implement that interface, a FlowableIllegalArgumentException is thrown naming the class and the missing interface. The library enforces this contract because it must invoke the listener's transaction-aware notification methods during commit/rollback phases.

Source

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

        CommandContextUtil.getProcessEngineConfiguration().getDelegateInterceptor().handleInvocation(new TaskListenerInvocation(taskListenerInstance, delegateTask));
    }

    @Override
    public void notify(String processInstanceId, String executionId, Task task, Map<String, Object> executionVariables, Map<String, Object> customPropertiesMap) {
        TransactionDependentTaskListener transactionDependentTaskListenerInstance = getTransactionDependentTaskListenerInstance();
        transactionDependentTaskListenerInstance.notify(processInstanceId, executionId, task, executionVariables, customPropertiesMap);
    }

    protected ExecutionListener getExecutionListenerInstance() {
        return new DelegateExecutionListener(instantiateDelegate(className, fieldDeclarations));
    }

    protected TransactionDependentExecutionListener getTransactionDependentExecutionListenerInstance() {
        Object delegateInstance = instantiateDelegate(className, fieldDeclarations);
        if (delegateInstance instanceof TransactionDependentExecutionListener) {
            return (TransactionDependentExecutionListener) delegateInstance;
        } else {
            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);

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Make the configured class implement org.flowable.engine.delegate.TransactionDependentExecutionListener (notify method taking TransactionDependentExecutionListenerExecution).
  2. Verify the class name in the BPMN XML matches the fully qualified name of the intended implementation.
  3. Rebuild/redeploy so the deployed classpath contains the compiled implementation of the interface.
  4. If you meant a plain listener, move the class to the flowable:executionListener attribute instead.

Example fix

// before
public class MyListener implements ExecutionListener { ... }
<!-- flowable:transactionDependentExecutionListener class="com.acme.MyListener" -->
// after
public class MyListener implements TransactionDependentExecutionListener {
    @Override
    public void notify(TransactionDependentExecutionListenerExecution execution) { ... }
}
Defensive patterns

Strategy: validation

Validate before calling

Class<?> c = Class.forName("com.acme.MyListener");
if (!TransactionDependentExecutionListener.class.isAssignableFrom(c))
    throw new IllegalStateException("Class must implement TransactionDependentExecutionListener");

Type guard

boolean isValidListener(Object o) { return o instanceof TransactionDependentExecutionListener; }

Try / catch

try {
    runtimeService.startProcessInstanceByKey("key");
} catch (FlowableIllegalArgumentException e) {
    if (e.getMessage().contains("TransactionDependentExecutionListener")) { /* fix deployment/class */ }
    throw e;
}

Prevention

When it happens

Trigger: A BPMN element (e.g. service task or user task) is configured with flowable:transactionDependentExecutionListener class='<delegateClassName>', and the referenced class (or a field-declaration instantiation of it) does not implement org.flowable.engine.delegate.TransactionDependentExecutionListener.

Common situations: Copy-pasting an ordinary ExecutionListener/JavaDelegate class into a transactionDependentExecutionListener attribute; renaming or refactoring the class so it no longer implements the interface; pointing the XML at the wrong class name; classpath collisions loading a stale version of the class.

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