flowable/flowable-engine · error · FlowableException
signal() can only be called on a TriggerableActivityBehavior
Error message
signal() can only be called on a TriggerableActivityBehavior instance for + execution
What it means
ClassDelegate.trigger() only forwards a signal to the delegate behavior when the resolved ActivityBehavior instance implements TriggerableActivityBehavior. If it does not (or the delegate resolved to null), a FlowableException is thrown stating signal() requires a TriggerableActivityBehavior for the given execution. This protects against signaling an activity whose behavior cannot be triggered.
Source
Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/ClassDelegate.java:241
if (!ErrorPropagation.mapException(e, (ExecutionEntity) execution, mapExceptions)) {
throw e;
}
}
} else if (activityBehaviorInstance instanceof TriggerableActivityBehavior) {
try {
((TriggerableActivityBehavior) activityBehaviorInstance).trigger(execution, signalName, signalData);
if (triggerable) {
leave(execution);
}
} catch (BusinessError error) {
ErrorPropagation.propagateError(error, execution);
} catch (RuntimeException e) {
if (!ErrorPropagation.mapException(e, (ExecutionEntity) execution, mapExceptions)) {
throw e;
}
}
} else {
throw new FlowableException("signal() can only be called on a " + TriggerableActivityBehavior.class.getName() + " instance for " + execution);
}
}
// Subprocess activityBehaviour
@Override
public void completing(DelegateExecution execution, DelegateExecution subProcessInstance) throws Exception {
if (activityBehaviorInstance == null) {
activityBehaviorInstance = getActivityBehaviorInstance();
}
if (activityBehaviorInstance instanceof SubProcessActivityBehavior) {
((SubProcessActivityBehavior) activityBehaviorInstance).completing(execution, subProcessInstance);
} else {
throw new FlowableException("completing() can only be called on a " + SubProcessActivityBehavior.class.getName() + " instance for " + execution);
}
}
View on GitHub (pinned to d6d39ce1c6)
Solutions
- Only call runtimeService.trigger() on executions whose current behavior implements TriggerableActivityBehavior; check the wait state first.
- Make the delegate's ActivityBehavior implement TriggerableActivityBehavior (signal method) if it should accept signals.
- Implement the delegate as a JavaDelegate with triggerable=true (ServiceTaskJavaDelegateActivityBehavior with triggerable) so signal support is wired.
- Fix process design: replace the signal with the correct completion path (e.g. complete the user task or use a message/signal event).
Example fix
// before
executionEntity.trigger(); // works for any wait state
// after
if (executionEntity.getCurrentFlowElement() instanceof ServiceTask
&& behavior instanceof TriggerableActivityBehavior) {
executionEntity.trigger();
} else {
// use the appropriate completion API for this wait state
} Defensive patterns
Strategy: type-guard
Validate before calling
Execution exec = runtimeService.createExecutionQuery().executionId(id).singleResult(); // only trigger executions waiting on a triggerable activity
Type guard
boolean canTrigger(ActivityBehavior b) { return b instanceof TriggerableActivityBehavior; } Try / catch
try {
runtimeService.trigger(executionId);
} catch (FlowableException e) {
if (e.getMessage().contains("TriggerableActivityBehavior")) { /* use correct completion API */ }
throw e;
} Prevention
- Check the current wait state's behavior before signaling.
- Set triggerable=true on service tasks that must accept signals.
- Model signal reception with proper intermediate catching events.
When it happens
Trigger: runtimeService.trigger(executionId) is called on an execution whose service task delegate class resolves to a non-triggerable ActivityBehavior (e.g. a plain JavaDelegate wrapped as non-triggerable, or behavior not implementing TriggerableActivityBehavior).
Common situations: Client code signals an execution that is waiting on a non-triggerable task; the delegate class was changed from an async/triggerable implementation to a plain one; BPMN model changes made the current wait state non-triggerable.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- completing() can only be called on a SubProcessActivityBehav
- completed() can only be called on a SubProcessActivityBehavi
- ${className} does not implement the ${CmmnTriggerableActivit
- ${className} does not implement the ${CmmnActivityBehavior.c
- CommandInvoker must be the last interceptor in the chain
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/3094a81b512afe6f.
Report an issue: GitHub.