flowable/flowable-engine · error · FlowableIllegalArgumentException
<delegateClassName> doesn't implement JavaDelegate, FutureJa
Error message
<delegateClassName> doesn't implement JavaDelegate, FutureJavaDelegate nor ActivityBehavior
What it means
ClassDelegate.getActivityBehaviorInstance() instantiates the delegate class and only accepts instances of ActivityBehavior, JavaDelegate, or FutureJavaDelegate, wrapping JavaDelegates in service-task behaviors. Anything else yields a FlowableIllegalArgumentException stating the class implements neither JavaDelegate, FutureJavaDelegate nor ActivityBehavior. This is the central contract check for class-based service task delegates.
Source
Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/ClassDelegate.java:283
if (activityBehaviorInstance instanceof SubProcessActivityBehavior) {
((SubProcessActivityBehavior) activityBehaviorInstance).completed(execution);
} else {
throw new FlowableException("completed() can only be called on a " + SubProcessActivityBehavior.class.getName() + " instance for " + execution);
}
}
protected ActivityBehavior getActivityBehaviorInstance() {
Object delegateInstance = instantiateDelegate(className, fieldDeclarations);
if (delegateInstance instanceof ActivityBehavior) {
return determineBehaviour((ActivityBehavior) delegateInstance);
} else if (delegateInstance instanceof JavaDelegate) {
return determineBehaviour(new ServiceTaskJavaDelegateActivityBehavior((JavaDelegate) delegateInstance, triggerable, skipExpression));
} else if (delegateInstance instanceof FutureJavaDelegate) {
return determineBehaviour(new ServiceTaskFutureJavaDelegateActivityBehavior((FutureJavaDelegate<?>) delegateInstance, triggerable, skipExpression, mapExceptions));
} else {
throw new FlowableIllegalArgumentException(delegateInstance.getClass().getName() + " doesn't implement " + JavaDelegate.class.getName() + ", " + FutureJavaDelegate.class.getName() + " nor " + ActivityBehavior.class.getName());
}
}
// Adds properties to the given delegation instance (eg multi instance) if needed
protected ActivityBehavior determineBehaviour(ActivityBehavior delegateInstance) {
if (hasMultiInstanceCharacteristics()) {
multiInstanceActivityBehavior.setInnerActivityBehavior((AbstractBpmnActivityBehavior) delegateInstance);
return multiInstanceActivityBehavior;
}
return delegateInstance;
}
}
View on GitHub (pinned to d6d39ce1c6)
Solutions
- Make the class implement org.flowable.engine.delegate.JavaDelegate (execute(DelegateExecution)) — the most common fix for service tasks.
- Alternatively implement FutureJavaDelegate or extend a Flowable ActivityBehavior for custom control flow.
- Verify the class attribute and imports reference Flowable types, not Activiti ones.
- Rebuild and redeploy so the engine loads the corrected class.
Example fix
// before
public class MyTask { public void run() { ... } }
<!-- flowable:class="com.acme.MyTask" -->
// after
public class MyTask implements JavaDelegate {
@Override public void execute(DelegateExecution execution) { ... }
} Defensive patterns
Strategy: validation
Validate before calling
Class<?> c = Class.forName(serviceTaskClass);
boolean ok = ActivityBehavior.class.isAssignableFrom(c)
|| JavaDelegate.class.isAssignableFrom(c)
|| FutureJavaDelegate.class.isAssignableFrom(c);
if (!ok) throw new IllegalStateException("Service task class must implement JavaDelegate/FutureJavaDelegate/ActivityBehavior"); Type guard
boolean isValidDelegate(Object o) {
return o instanceof ActivityBehavior || o instanceof JavaDelegate || o instanceof FutureJavaDelegate;
} Try / catch
try {
runtimeService.startProcessInstanceByKey("proc");
} catch (FlowableIllegalArgumentException e) {
if (e.getMessage().contains("JavaDelegate")) { /* fix service task class */ }
throw e;
} Prevention
- Default to implementing JavaDelegate for service tasks.
- Never point flowable:class at arbitrary beans.
- Add an engine test executing every service task path before release.
When it happens
Trigger: A service task (or other behavior-backed element) declares flowable:class='<delegateClassName>' and the class implements none of ActivityBehavior, JavaDelegate (execute(DelegateExecution)), or FutureJavaDelegate; also fired by execute/trigger/completing/completed when the behavior instance is first resolved.
Common situations: Pointing flowable:class at an arbitrary helper bean; missing import so the class implements org.activiti.engine.impl.pvm.delegate.ActivityBehavior or wrong package; the class implements only a listener interface; wrong fully qualified name resolving to an unrelated 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
- <delegateClassName> doesn't implement TransactionDependentEx
- <delegateClassName> doesn't implement CustomPropertiesResolv
- <delegateClassName> doesn't implement TaskListener
- <delegateClassName> doesn't implement TransactionDependentTa
- <delegateClassName> doesn't implement FlowableCollectionHand
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/2cbb0e409ae2386f.
Report an issue: GitHub.