flowable/flowable-engine · error · FlowableException
Cannot trigger ${execution} : the activityBehavior ${activit
Error message
Cannot trigger ${execution} : the activityBehavior ${activityBehavior.getClass()} does not implement the org.flowable.engine.impl.delegate.TriggerableActivityBehavior interface What it means
TriggerExecutionOperation.run resumes a waiting execution (e.g. after signal/message/trigger). The current flow element's behavior must implement TriggerableActivityBehavior to be triggered; if it does not, the engine cannot continue the execution and throws this FlowableException.
Source
Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/agenda/TriggerExecutionOperation.java:72
ActivityBehavior activityBehavior = (ActivityBehavior) ((FlowNode) currentFlowElement).getBehavior();
if (activityBehavior instanceof TriggerableActivityBehavior) {
if (!triggerAsync) {
((TriggerableActivityBehavior) activityBehavior).trigger(execution, null, null);
} else {
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
JobService jobService = processEngineConfiguration.getJobServiceConfiguration().getJobService();
JobEntity job = JobUtil.createJob(execution, currentFlowElement, AsyncTriggerJobHandler.TYPE, processEngineConfiguration);
jobService.createAsyncJob(job, true);
jobService.scheduleAsyncJob(job);
}
} else {
throw new FlowableException("Cannot trigger " + execution
+ " : the activityBehavior " + activityBehavior.getClass() + " does not implement the "
+ TriggerableActivityBehavior.class.getName() + " interface");
}
} else if (currentFlowElement == null) {
throw new FlowableException("Cannot trigger " + execution
+ " : no current flow element found. Check the execution id that is being passed "
+ "(it should not be a process instance execution, but a child execution currently referencing a flow element).");
} else {
throw new FlowableException("Programmatic error: cannot trigger " + execution + ", invalid flow element type found: "
+ currentFlowElement.getClass().getName() + ".");
}
}
}View on GitHub (pinned to d6d39ce1c6)
Solutions
- Query the execution and verify its activityId is a waiting state (receive task, intermediate catch event) before triggering
- Ensure custom ActivityBehaviors implement TriggerableActivityBehavior when they must be triggerable
- Guard against double-triggers with an optimistic-lock/race check
- Use event subscription queries (signalEventReceived/messageEventReceived) instead of raw trigger when appropriate
Example fix
// before
Execution e = runtimeService.createExecutionQuery().processInstanceId(pid).singleResult();
runtimeService.trigger(e.getId());
// after
Execution e = runtimeService.createExecutionQuery()
.processInstanceId(pid)
.activityId("waitReceiveTask")
.singleResult();
if (e != null) {
runtimeService.trigger(e.getId());
} Defensive patterns
Strategy: validation
Validate before calling
Execution e = runtimeService.createExecutionQuery()
.processInstanceId(pid)
.activityId("waitForSignal")
.singleResult();
if (e == null) {
throw new IllegalStateException("Execution not waiting at waitForSignal");
}
runtimeService.trigger(e.getId()); Type guard
boolean isWaitingAtTriggerable(Execution e, String activityId) {
return e != null && activityId.equals(e.getActivityId());
} Try / catch
try {
runtimeService.trigger(executionId);
} catch (FlowableException e) {
if (e.getMessage().contains("does not implement") && e.getMessage().contains("TriggerableActivityBehavior")) {
log.error("Execution {} is not at a triggerable activity; re-check query", executionId);
}
throw e;
} Prevention
- Query executions by activityId, not just processInstanceId
- Handle idempotency for double-trigger races
- Implement TriggerableActivityBehavior in custom waiting behaviors
- Prefer signal/message-specific APIs over generic trigger
When it happens
Trigger: Calling runtimeService.trigger(executionId) (or signal/message received) on an execution whose current activity behavior is not TriggerableActivityBehavior — e.g. triggering the wrong execution id, or an execution parked on a non-waiting element.
Common situations: Triggering an execution selected by the wrong query (not actually waiting at a receive task/user task); race conditions where the execution already moved on; custom behaviors not implementing the right interface.
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
- text is null
- bytes array is null
- problem reading zip input stream
- Deployment id is null
- Deployment ids is null
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/225384e1a18d6a6a.
Report an issue: GitHub.