flowable/flowable-engine · error · FlowableException
completed() can only be called on a SubProcessActivityBehavi
Error message
completed() can only be called on a SubProcessActivityBehavior instance for + execution
What it means
ClassDelegate.completed() forwards the subprocess-finished callback only when the resolved ActivityBehavior implements SubProcessActivityBehavior; otherwise it throws a FlowableException stating completed() requires a SubProcessActivityBehavior. This mirrors the completing() check on the parent-execution side after the subprocess instance has ended.
Source
Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/ClassDelegate.java:269
}
if (activityBehaviorInstance instanceof SubProcessActivityBehavior) {
((SubProcessActivityBehavior) activityBehaviorInstance).completing(execution, subProcessInstance);
} else {
throw new FlowableException("completing() can only be called on a " + SubProcessActivityBehavior.class.getName() + " instance for " + execution);
}
}
@Override
public void completed(DelegateExecution execution) throws Exception {
if (activityBehaviorInstance == null) {
activityBehaviorInstance = getActivityBehaviorInstance();
}
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 neededView on GitHub (pinned to d6d39ce1c6)
Solutions
- Make the delegate behavior implement SubProcessActivityBehavior (or extend AbstractBpmnActivityBehavior).
- Confirm the BPMN element uses a delegate class intended for subprocess semantics.
- Restrict custom calls to completed() to subprocess-configured delegates.
- Rebuild/redeploy after any delegate class refactor to keep the process definition consistent.
Example fix
// before
public class MyBehavior implements ActivityBehavior { public void execute(...) {...} }
// after
public class MyBehavior extends AbstractBpmnActivityBehavior {
@Override public void completed(DelegateExecution execution) { ... }
} Defensive patterns
Strategy: type-guard
Validate before calling
if (!(behavior instanceof SubProcessActivityBehavior)) throw new IllegalStateException("Delegate must be a SubProcessActivityBehavior"); Type guard
boolean isSubProcessBehavior(ActivityBehavior b) { return b instanceof SubProcessActivityBehavior; } Try / catch
try {
runtimeService.startProcessInstanceByKey("proc");
} catch (FlowableException e) {
if (e.getMessage().contains("completed() can only be called")) { /* fix delegate class */ }
throw e;
} Prevention
- Same as completing(): use AbstractBpmnActivityBehavior subclasses for subprocess delegates.
- Test subprocess completion in engine unit tests.
- Do not refactor subprocess behaviors to plain ActivityBehavior.
When it happens
Trigger: A subprocess child activity's ClassDelegate resolves to a behavior that is not a SubProcessActivityBehavior, and completed(execution) is invoked when the subprocess instance completes and control returns to the parent scope.
Common situations: Same family as 2105: wrong delegate class on a subprocess element, refactoring away from AbstractBpmnActivityBehavior, or custom orchestration code invoking completed() on a plain delegate.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- completing() can only be called on a SubProcessActivityBehav
- signal() can only be called on a TriggerableActivityBehavior
- ${className} does not implement the ${CmmnTriggerableActivit
- ${className} does not implement the ${CmmnActivityBehavior.c
- CommandInvoker must be the last interceptor in the chain
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/4ac7d9edecb0e0c8.
Report an issue: GitHub.