flowable/flowable-engine · error · ActivitiException

signal() can only be called on a %s instance

Error message

signal() can only be called on a %s instance

What it means

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.

Source

Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/helper/ClassDelegate.java:178

        } catch (BpmnError error) {
            ErrorPropagation.propagateError(error, activityExecution);
        } catch (RuntimeException e) {
            if (!ErrorPropagation.mapException(e, activityExecution, mapExceptions))
                throw e;
        }
    }

    // Signallable activity behavior
    @Override
    public void signal(ActivityExecution execution, String signalName, Object signalData) throws Exception {
        if (activityBehaviorInstance == null) {
            activityBehaviorInstance = getActivityBehaviorInstance(execution);
        }

        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");
        }
    }

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Make the configured delegate class implement org.activiti.engine.impl.bpmn.behavior.SignallableActivityBehavior and provide a signal(ActivityExecution, String, Object) method.
  2. Verify the className/fieldDeclarations configured on the ClassDelegate point at the intended class (inspect getActivityBehaviorInstance/determineBehaviour output).
  3. 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.

Example fix

// before
public class MyDelegate implements ActivityBehavior {
  public void execute(ActivityExecution execution) { ... }
}
// after
public class MyDelegate implements ActivityBehavior, SignallableActivityBehavior {
  public void execute(ActivityExecution execution) { ... }
  public void signal(ActivityExecution execution, String signalName, Object signalData) { ... }
}
Defensive patterns

Strategy: type-guard

Validate before calling

Object delegate = ReflectUtil.instantiate(className);
if (!(delegate instanceof SignallableActivityBehavior)) {
  throw new IllegalStateException(className + " must implement SignallableActivityBehavior before signaling");
}

Type guard

boolean canSignal(ClassDelegate d) {
  try { return d.getActivityBehaviorInstance(nullDelegateExec()) instanceof SignallableActivityBehavior; }
  catch (Exception e) { return false; }
}

Try / catch

try {
  classDelegate.signal(execution, signalName, signalData);
} catch (ActivitiException e) {
  if (e.getMessage().contains("SignallableActivityBehavior")) {
    log.error("Delegate {} is not signallable; fix delegate class", className, e);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling signal(execution, signalName, signalData) on a ClassDelegate whose configured className resolves to an instance that implements only ActivityBehavior or JavaDelegate, not SignallableActivityBehavior.

Common situations: 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.

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/1e9896493efa7228. Report an issue: GitHub.