flowable/flowable-engine · error · FlowableIllegalStateException
Can only trigger an event listener plan item that is in the
Error message
Can only trigger an event listener plan item that is in the AVAILABLE state
What it means
TriggerPlanItemInstanceOperation.run executes the trigger only when the plan item instance has a PlanItem definition and is in the AVAILABLE state (for event listeners) or ACTIVE state (for others). Triggering an event listener that is not AVAILABLE is an illegal transition, so a FlowableIllegalStateException is thrown.
Source
Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/agenda/operation/TriggerPlanItemInstanceOperation.java:45
*/
public class TriggerPlanItemInstanceOperation extends AbstractPlanItemInstanceOperation {
public TriggerPlanItemInstanceOperation(CommandContext commandContext, PlanItemInstanceEntity planItemInstanceEntity) {
super(commandContext, planItemInstanceEntity);
}
@Override
public void run() {
if (PlanItemInstanceState.ACTIVE.equals(planItemInstanceEntity.getState())
|| (planItemInstanceEntity.getPlanItem() != null
&& planItemInstanceEntity.getPlanItem().getPlanItemDefinition() instanceof EventListener
&& PlanItemInstanceState.AVAILABLE.equals(planItemInstanceEntity.getState()))){
executeTrigger();
} else {
if (planItemInstanceEntity.getPlanItem() != null && planItemInstanceEntity.getPlanItem().getPlanItemDefinition() instanceof EventListener){
throw new FlowableIllegalStateException("Can only trigger an event listener plan item that is in the AVAILABLE state");
} else {
throw new FlowableIllegalStateException("Can only trigger a plan item that is in the ACTIVE state");
}
}
}
protected void executeTrigger() {
Object behaviorObject = planItemInstanceEntity.getPlanItem().getBehavior();
if (!(behaviorObject instanceof CmmnTriggerableActivityBehavior)) {
throw new FlowableException("Cannot trigger a plan item which activity behavior does not implement the "
+ CmmnTriggerableActivityBehavior.class + " interface in " + planItemInstanceEntity);
}
CmmnTriggerableActivityBehavior behavior = (CmmnTriggerableActivityBehavior) planItemInstanceEntity.getPlanItem().getBehavior();
if (behavior instanceof CoreCmmnTriggerableActivityBehavior) {
((CoreCmmnTriggerableActivityBehavior) behavior).trigger(commandContext, planItemInstanceEntity);
} else {
behavior.trigger(planItemInstanceEntity);View on GitHub (pinned to d6d39ce1c6)
Solutions
- Check the plan item instance state equals 'available' before triggering.
- Make client-side triggers idempotent (disable the button after first call).
- Catch FlowableIllegalStateException and treat repeat triggers as no-ops.
- Query fresh state within the same command to avoid races.
Example fix
// before
cmmnRuntimeService.triggerPlanItemInstance(planItemInstanceId);
// after
PlanItemInstance pii = cmmnRuntimeService.createPlanItemInstanceQuery()
.planItemInstanceId(planItemInstanceId).singleResult();
if (pii != null && "available".equals(pii.getState())) {
cmmnRuntimeService.triggerPlanItemInstance(planItemInstanceId);
} Defensive patterns
Strategy: validation
Validate before calling
// java
PlanItemInstance pii = cmmnRuntimeService.createPlanItemInstanceQuery()
.planItemInstanceId(planItemInstanceId).singleResult();
if (pii == null || !"available".equals(pii.getState())) return; // cannot trigger Type guard
boolean isTriggerable(PlanItemInstance pii) {
return pii != null && PlanItemInstanceState.AVAILABLE.equals(pii.getState());
} Try / catch
try {
cmmnRuntimeService.triggerPlanItemInstance(id);
} catch (FlowableIllegalStateException e) {
if (e.getMessage().contains("Can only trigger")) {
// already triggered or wrong state: idempotent no-op
}
} Prevention
- Disable trigger/complete controls after first submission.
- Re-read plan item state inside the same transaction as the trigger.
- Treat duplicate triggers as idempotent no-ops server-side.
When it happens
Trigger: Calling cmmnRuntimeService.triggerPlanItemInstance (or complete of a user event listener) when the event listener instance is already triggered/completed/terminated, or is in a state other than AVAILABLE.
Common situations: Double-click / duplicate submit completing the same user event listener; retry logic re-triggering after a first successful trigger; triggering a listener on a suspended or completed case.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Cannot override built-in eventListenerType '{listenerType}'
- Cannot exit stage with 'complete' event type as the stage '{
- plan item instance can only be resumed if the state is suspe
- plan item instance is already suspended
- Cannot exit case with 'complete' event type as the case '${c
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/3e49173dbe6fbcba.
Report an issue: GitHub.