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

  1. Implement org.activiti.engine.impl.bpmn.behavior.SubProcessActivityBehavior (completing and completed methods) in the configured delegate class.
  2. Check the BPMN XML/bean config so the subprocess activity uses the correct SubProcessActivityBehavior delegate class.
  3. 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

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


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