{"record":{"id":"4ac7d9edecb0e0c8","repo":"flowable/flowable-engine","slug":"completed-can-only-be-called-on-a-subprocessacti","errorCode":null,"errorMessage":"completed() can only be called on a SubProcessActivityBehavior instance for + execution","messagePattern":"completed\\(\\) can only be called on a SubProcessActivityBehavior instance for \\+ execution","errorType":"exception","errorClass":"FlowableException","httpStatus":null,"severity":"error","filePath":"modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/ClassDelegate.java","lineNumber":269,"sourceCode":"        }\n\n        if (activityBehaviorInstance instanceof SubProcessActivityBehavior) {\n            ((SubProcessActivityBehavior) activityBehaviorInstance).completing(execution, subProcessInstance);\n        } else {\n            throw new FlowableException(\"completing() can only be called on a \" + SubProcessActivityBehavior.class.getName() + \" instance for \" + execution);\n        }\n    }\n\n    @Override\n    public void completed(DelegateExecution execution) throws Exception {\n        if (activityBehaviorInstance == null) {\n            activityBehaviorInstance = getActivityBehaviorInstance();\n        }\n\n        if (activityBehaviorInstance instanceof SubProcessActivityBehavior) {\n            ((SubProcessActivityBehavior) activityBehaviorInstance).completed(execution);\n        } else {\n            throw new FlowableException(\"completed() can only be called on a \" + SubProcessActivityBehavior.class.getName() + \" instance for \" + execution);\n        }\n    }\n\n    protected ActivityBehavior getActivityBehaviorInstance() {\n        Object delegateInstance = instantiateDelegate(className, fieldDeclarations);\n\n        if (delegateInstance instanceof ActivityBehavior) {\n            return determineBehaviour((ActivityBehavior) delegateInstance);\n        } else if (delegateInstance instanceof JavaDelegate) {\n            return determineBehaviour(new ServiceTaskJavaDelegateActivityBehavior((JavaDelegate) delegateInstance, triggerable, skipExpression));\n        } else if (delegateInstance instanceof FutureJavaDelegate) {\n            return determineBehaviour(new ServiceTaskFutureJavaDelegateActivityBehavior((FutureJavaDelegate<?>) delegateInstance, triggerable, skipExpression, mapExceptions));\n        } else {\n            throw new FlowableIllegalArgumentException(delegateInstance.getClass().getName() + \" doesn't implement \" + JavaDelegate.class.getName() + \", \" + FutureJavaDelegate.class.getName() + \" nor \" + ActivityBehavior.class.getName());\n        }\n    }\n\n    // Adds properties to the given delegation instance (eg multi instance) if needed","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/ClassDelegate.java#L251-L287","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\npublic class MyBehavior implements ActivityBehavior { public void execute(...) {...} }\n// after\npublic class MyBehavior extends AbstractBpmnActivityBehavior {\n    @Override public void completed(DelegateExecution execution) { ... }\n}","handlingStrategy":"type-guard","validationCode":"if (!(behavior instanceof SubProcessActivityBehavior)) throw new IllegalStateException(\"Delegate must be a SubProcessActivityBehavior\");","typeGuard":"boolean isSubProcessBehavior(ActivityBehavior b) { return b instanceof SubProcessActivityBehavior; }","tryCatchPattern":"try {\n    runtimeService.startProcessInstanceByKey(\"proc\");\n} catch (FlowableException e) {\n    if (e.getMessage().contains(\"completed() can only be called\")) { /* fix delegate class */ }\n    throw e;\n}","preventionTips":["Same as completing(): use AbstractBpmnActivityBehavior subclasses for subprocess delegates.","Test subprocess completion in engine unit tests.","Do not refactor subprocess behaviors to plain ActivityBehavior."],"tags":["flowable","delegate","subprocess","unsupported-operation"],"backgroundTag":"unsupported-operation","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}