flowable/flowable-engine · error · ActivitiException
completing() can only be called on a %s instance
Error message
completing() can only be called on a %s instance
What it means
ClassDelegate forwards subprocess lifecycle calls to the resolved delegate instance. completing() is only meaningful for a SubProcessActivityBehavior, so when the resolved delegate does not implement that interface, an ActivitiException is thrown with the fully-qualified interface name. It guards the call cast ((SubProcessActivityBehavior) ...) from failing with an opaque ClassCastException.
Source
Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/helper/ClassDelegate.java:193
if (activityBehaviorInstance instanceof SignallableActivityBehavior) {
((SignallableActivityBehavior) activityBehaviorInstance).signal(execution, signalName, signalData);
} else {
throw new ActivitiException("signal() can only be called on a " + SignallableActivityBehavior.class.getName() + " instance");
}
}
// Subprocess activityBehaviour
@Override
public void completing(DelegateExecution execution, DelegateExecution subProcessInstance) throws Exception {
if (activityBehaviorInstance == null) {
activityBehaviorInstance = getActivityBehaviorInstance((ActivityExecution) execution);
}
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);View on GitHub (pinned to d6d39ce1c6)
Solutions
- Implement org.activiti.engine.impl.bpmn.behavior.SubProcessActivityBehavior (completing and completed methods) in the configured delegate class.
- Check the BPMN XML/bean config so the subprocess activity uses the correct SubProcessActivityBehavior delegate class.
- If the delegate is meant to be a simple task, move it out of the subprocess-completion path that triggers completing().
Example fix
// before
public class SubDelegate implements ActivityBehavior { ... }
// after
public class SubDelegate implements SubProcessActivityBehavior {
public void completing(ActivityExecution execution, ActivityExecution subProcessInstance) { ... }
public void completed(ActivityExecution execution) { ... }
} 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 isSubProcessDelegate(Object delegate) { return delegate instanceof SubProcessActivityBehavior; } Try / catch
try {
classDelegate.completing(execution, subProcessInstance);
} catch (ActivitiException e) {
if (e.getMessage().contains("SubProcessActivityBehavior")) {
log.error("Wrong delegate class for subprocess activity", e);
} else throw e;
} Prevention
- Validate delegate interfaces at deployment time, not first execution
- Use distinct delegate naming conventions for subprocess vs task delegates
- Review BPMN XML of subprocess activities after refactors
When it happens
Trigger: A subprocess (call-activity/embedded subprocess) whose ClassDelegate's className resolves to a delegate that does not implement SubProcessActivityBehavior, and the subprocess completes causing completing(execution, subProcessInstance) to be invoked.
Common situations: Misconfigured subprocess delegate: developer pointed the activity at a plain ActivityBehavior or JavaDelegate; refactoring renamed the SubProcessActivityBehavior implementation; copy-pasted delegate config from a service task into a subprocess boundary event handler.
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
- completed() 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/8880ea33dce5af78.
Report an issue: GitHub.