flowable/flowable-engine · error · FlowableIllegalStateException
Can only trigger a human task plan item that is in the ACTIV
Error message
Can only trigger a human task plan item that is in the ACTIVE state
What it means
trigger() completes a human task plan item programmatically (e.g. via RuntimeService.triggerPlanItemInstance). It is only legal while the plan item instance is in the ACTIVE state; calling it in any other lifecycle state (AVAILABLE, ENABLED, COMPLETED, TERMINATED, SUSPENDED) throws FlowableIllegalStateException to prevent completing tasks out of order.
Source
Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/HumanTaskActivityBehavior.java:469
}
}
}
}
private void handleTaskIdVariableStorage(PlanItemInstanceEntity planItemInstanceEntity, HumanTask humanTask, ExpressionManager expressionManager, TaskEntity taskEntity) {
if (StringUtils.isNotEmpty(humanTask.getTaskIdVariableName())) {
Expression expression = expressionManager.createExpression(humanTask.getTaskIdVariableName());
String idVariableName = (String) expression.getValue(planItemInstanceEntity);
if (StringUtils.isNotEmpty(idVariableName)) {
planItemInstanceEntity.setVariable(idVariableName, taskEntity.getId());
}
}
}
@Override
public void trigger(CommandContext commandContext, PlanItemInstanceEntity planItemInstance) {
if (!PlanItemInstanceState.ACTIVE.equals(planItemInstance.getState())) {
throw new FlowableIllegalStateException("Can only trigger a human task plan item that is in the ACTIVE state");
}
CmmnEngineConfiguration cmmnEngineConfiguration = CommandContextUtil.getCmmnEngineConfiguration(commandContext);
TaskService taskService = cmmnEngineConfiguration.getTaskServiceConfiguration().getTaskService();
List<TaskEntity> taskEntities = taskService.findTasksBySubScopeIdScopeType(planItemInstance.getId(), ScopeTypes.CMMN);
if (taskEntities == null || taskEntities.isEmpty()) {
throw new FlowableException("No task entity found for " + planItemInstance);
}
// Should be only one
for (TaskEntity taskEntity : taskEntities) {
if (!taskEntity.isDeleted()) {
TaskHelper.completeTask(taskEntity, taskEntity.getTempCompletedBy(), cmmnEngineConfiguration);
}
}
CommandContextUtil.getAgenda(commandContext).planCompletePlanItemInstanceOperation(planItemInstance);
}View on GitHub (pinned to d6d39ce1c6)
Solutions
- Check planItemInstance.getState() equals ACTIVE before calling triggerPlanItemInstance and skip/no-op otherwise.
- Re-fetch the plan item instance fresh inside the same command/transaction before triggering to avoid stale state.
- Treat it as expected in idempotent flows: catch FlowableIllegalStateException and verify the task was already completed.
- Serialize triggering through a single entry point (e.g. job/queue) to remove the race.
Example fix
// before
runtimeService.triggerPlanItemInstance(planItemInstanceId);
// after
PlanItemInstance pii = cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceId(id).singleResult();
if (pii != null && PlanItemInstanceState.ACTIVE.equals(pii.getState())) {
runtimeService.triggerPlanItemInstance(id);
} Defensive patterns
Strategy: validation
Validate before calling
PlanItemInstance pii = cmmnRuntimeService.createPlanItemInstanceQuery()
.planItemInstanceInstanceId(id).singleResult();
if (pii == null || !PlanItemInstanceState.ACTIVE.equals(pii.getState())) {
throw new IllegalStateException("Cannot trigger plan item " + id + " in state " + (pii == null ? "<missing>" : pii.getState()));
} Type guard
boolean isTriggerable(PlanItemInstance pii) { return pii != null && PlanItemInstanceState.ACTIVE.equals(pii.getState()); } Try / catch
try {
runtimeService.triggerPlanItemInstance(id);
} catch (FlowableIllegalStateException e) {
if (e.getMessage().contains("Can only trigger a human task plan item")) {
log.warn("Plan item {} already completed or not active; ignoring", id);
}
} Prevention
- Always re-query plan item state in the same transaction as the trigger
- Design trigger endpoints as idempotent
- Guard UI actions by refreshing state before enabling the trigger button
- Avoid concurrent triggers on the same plan item instance
When it happens
Trigger: Calling triggerPlanItemInstance (or the behavior's trigger) on a plan item whose state is not ACTIVE — typically a second trigger after completion, or triggering an item still waiting in an available/enabled stage.
Common situations: Race condition where two threads/users trigger the same task; retrying after an earlier trigger already completed the item; assuming a milestone-stage item was active when it was merely enabled; stale UI state in a task list.
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
- Case instance is still running, cannot delete
- Tenant mismatch between Case Instance ('${caseInstance.getTe
- The ${task} cannot be deleted because is part of a running c
- Setting variable is not supported for read only delegate exe
- Setting transient variable is not supported for read only de
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/11f1ee82d33736b2.
Report an issue: GitHub.