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 needed

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Make the delegate behavior implement SubProcessActivityBehavior (or extend AbstractBpmnActivityBehavior).
  2. Confirm the BPMN element uses a delegate class intended for subprocess semantics.
  3. Restrict custom calls to completed() to subprocess-configured delegates.
  4. 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

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


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/4ac7d9edecb0e0c8. Report an issue: GitHub.