{"record":{"id":"1e9896493efa7228","repo":"flowable/flowable-engine","slug":"signal-can-only-be-called-on-a-s-instance","errorCode":null,"errorMessage":"signal() can only be called on a %s instance","messagePattern":"signal\\(\\) can only be called on a (.+?) instance","errorType":"exception","errorClass":"ActivitiException","httpStatus":null,"severity":"error","filePath":"modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/helper/ClassDelegate.java","lineNumber":178,"sourceCode":"        } catch (BpmnError error) {\n            ErrorPropagation.propagateError(error, activityExecution);\n        } catch (RuntimeException e) {\n            if (!ErrorPropagation.mapException(e, activityExecution, mapExceptions))\n                throw e;\n        }\n    }\n\n    // Signallable activity behavior\n    @Override\n    public void signal(ActivityExecution execution, String signalName, Object signalData) throws Exception {\n        if (activityBehaviorInstance == null) {\n            activityBehaviorInstance = getActivityBehaviorInstance(execution);\n        }\n\n        if (activityBehaviorInstance instanceof SignallableActivityBehavior) {\n            ((SignallableActivityBehavior) activityBehaviorInstance).signal(execution, signalName, signalData);\n        } else {\n            throw new ActivitiException(\"signal() can only be called on a \" + SignallableActivityBehavior.class.getName() + \" instance\");\n        }\n    }\n\n    // Subprocess activityBehaviour\n\n    @Override\n    public void completing(DelegateExecution execution, DelegateExecution subProcessInstance) throws Exception {\n        if (activityBehaviorInstance == null) {\n            activityBehaviorInstance = getActivityBehaviorInstance((ActivityExecution) execution);\n        }\n\n        if (activityBehaviorInstance instanceof SubProcessActivityBehavior) {\n            ((SubProcessActivityBehavior) activityBehaviorInstance).completing(execution, subProcessInstance);\n        } else {\n            throw new ActivitiException(\"completing() can only be called on a \" + SubProcessActivityBehavior.class.getName() + \" instance\");\n        }\n    }\n","sourceCodeStart":160,"sourceCodeEnd":196,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/helper/ClassDelegate.java#L160-L196","documentation":"ClassDelegate is a proxy that loads a user-configured delegate class and forwards behavior calls to it. When signal() is invoked, it checks at runtime that the resolved delegate instance implements SignallableActivityBehavior; if not, this ActivitiException is thrown. The library throws it because signaling a delegate that has no signal-handling behavior is meaningless and would silently do nothing otherwise.","triggerScenarios":"Calling signal(execution, signalName, signalData) on a ClassDelegate whose configured className resolves to an instance that implements only ActivityBehavior or JavaDelegate, not SignallableActivityBehavior.","commonSituations":"A BPMN process reached a service task or receive-task-like activity whose delegate class was swapped or misconfigured; a developer changed the delegate class from a SignallableActivityBehavior implementation to a plain JavaDelegate and an external trigger then signals the execution; wrong delegateExpression pointing at the wrong bean.","solutions":["Make the configured delegate class implement org.activiti.engine.impl.bpmn.behavior.SignallableActivityBehavior and provide a signal(ActivityExecution, String, Object) method.","Verify the className/fieldDeclarations configured on the ClassDelegate point at the intended class (inspect getActivityBehaviorInstance/determineBehaviour output).","If the activity should not be signaled, remove the trigger that calls signal() (e.g. the message/signal event or Execution.signal) or use execute() path instead."],"exampleFix":"// before\npublic class MyDelegate implements ActivityBehavior {\n  public void execute(ActivityExecution execution) { ... }\n}\n// after\npublic class MyDelegate implements ActivityBehavior, SignallableActivityBehavior {\n  public void execute(ActivityExecution execution) { ... }\n  public void signal(ActivityExecution execution, String signalName, Object signalData) { ... }\n}","handlingStrategy":"type-guard","validationCode":"Object delegate = ReflectUtil.instantiate(className);\nif (!(delegate instanceof SignallableActivityBehavior)) {\n  throw new IllegalStateException(className + \" must implement SignallableActivityBehavior before signaling\");\n}","typeGuard":"boolean canSignal(ClassDelegate d) {\n  try { return d.getActivityBehaviorInstance(nullDelegateExec()) instanceof SignallableActivityBehavior; }\n  catch (Exception e) { return false; }\n}","tryCatchPattern":"try {\n  classDelegate.signal(execution, signalName, signalData);\n} catch (ActivitiException e) {\n  if (e.getMessage().contains(\"SignallableActivityBehavior\")) {\n    log.error(\"Delegate {} is not signallable; fix delegate class\", className, e);\n  } else throw e;\n}","preventionTips":["Only wire signaling (message/signal events) to delegates implementing SignallableActivityBehavior","Add a startup test that instantiates every configured delegate and asserts its interfaces","Keep delegate classnames centralized in constants to avoid config drift"],"tags":["delegate","bpmn","runtime","type-mismatch"],"backgroundTag":"type-mismatch","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"}