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

  1. Make the delegate class implement SubProcessActivityBehavior including completed(ActivityExecution).
  2. Fix the className on the ClassDelegate (or the classpath resource) so the intended SubProcessActivityBehavior implementation is instantiated.
  3. 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

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


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