flowable/flowable-engine · error · ActivitiException
completed() can only be called on a %s instance
Error message
completed() can only be called on a %s instance
What it means
completed() is the second half of the SubProcessActivityBehavior contract: it is called after the subprocess instance has finished. ClassDelegate checks the resolved delegate implements SubProcessActivityBehavior before casting; otherwise it throws this ActivitiException naming the required interface.
Source
Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/helper/ClassDelegate.java:206
}
if (activityBehaviorInstance instanceof SubProcessActivityBehavior) {
((SubProcessActivityBehavior) activityBehaviorInstance).completing(execution, subProcessInstance);
} else {
throw new ActivitiException("completing() can only be called on a " + SubProcessActivityBehavior.class.getName() + " instance");
}
}
@Override
public void completed(ActivityExecution execution) throws Exception {
if (activityBehaviorInstance == null) {
activityBehaviorInstance = getActivityBehaviorInstance(execution);
}
if (activityBehaviorInstance instanceof SubProcessActivityBehavior) {
((SubProcessActivityBehavior) activityBehaviorInstance).completed(execution);
} else {
throw new ActivitiException("completed() can only be called on a " + SubProcessActivityBehavior.class.getName() + " instance");
}
}
protected ActivityBehavior getActivityBehaviorInstance(ActivityExecution execution) {
Object delegateInstance = instantiateDelegate(className, fieldDeclarations);
if (delegateInstance instanceof ActivityBehavior) {
return determineBehaviour((ActivityBehavior) delegateInstance, execution);
} else if (delegateInstance instanceof JavaDelegate) {
return determineBehaviour(new ServiceTaskJavaDelegateActivityBehavior((JavaDelegate) delegateInstance, skipExpression), execution);
} else {
throw new ActivitiIllegalArgumentException(delegateInstance.getClass().getName() + " doesn't implement " + JavaDelegate.class.getName() + " nor " + ActivityBehavior.class.getName());
}
}
// Adds properties to the given delegation instance (eg multi instance) if needed
protected ActivityBehavior determineBehaviour(ActivityBehavior delegateInstance, ActivityExecution execution) {
if (hasMultiInstanceCharacteristics()) {View on GitHub (pinned to d6d39ce1c6)
Solutions
- Make the delegate class implement SubProcessActivityBehavior including completed(ActivityExecution).
- Fix the className on the ClassDelegate (or the classpath resource) so the intended SubProcessActivityBehavior implementation is instantiated.
- Review recent deployments/jars for a duplicate or stale class shadowing the correct implementation.
Example fix
// before
public class SubDelegate implements JavaDelegate {
public void execute(DelegateExecution e) { ... }
}
// after
public class SubDelegate implements SubProcessActivityBehavior {
public void completed(ActivityExecution execution) { ... }
public void completing(ActivityExecution execution, ActivityExecution subProcessInstance) { ... }
} Defensive patterns
Strategy: validation
Validate before calling
Class<?> clazz = Class.forName(delegateClassName);
if (!SubProcessActivityBehavior.class.isAssignableFrom(clazz)) {
throw new IllegalStateException(delegateClassName + " must implement SubProcessActivityBehavior");
} Type guard
boolean handlesSubProcessCompletion(Object delegate) { return delegate instanceof SubProcessActivityBehavior; } Try / catch
try {
classDelegate.completed(execution);
} catch (ActivitiException e) {
if (e.getMessage().contains("SubProcessActivityBehavior")) {
log.error("Subprocess completion delegate misconfigured", e);
} else throw e;
} Prevention
- Implement both completing() and completed() together when writing SubProcessActivityBehavior delegates
- Add integration tests that run subprocesses to completion with each delegate
- Avoid reusing plain service-task delegates on subprocess boundary activities
When it happens
Trigger: A subprocess ends and the engine calls completed(execution) on the ClassDelegate, whose resolved delegate instance is not a SubProcessActivityBehavior (e.g. it only implements ActivityBehavior or JavaDelegate).
Common situations: Same root cause as the completing() error: wrong delegate class configured for a subprocess activity; the delegate was replaced during deployment with an incompatible one; runtime reload picks up a different 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
- completing() can only be called on a %s instance
- Programmatic error: sub process behaviour can only be applie
- signal() can only be called on a %s instance
- ${delegateInstance.getClass().getName()} doesn't implement $
- ${delegateInstance.getClass().getName()} doesn't implement $
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/07a18052cbf3ef5f.
Report an issue: GitHub.