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

  1. Check planItemInstance.getState() equals ACTIVE before calling triggerPlanItemInstance and skip/no-op otherwise.
  2. Re-fetch the plan item instance fresh inside the same command/transaction before triggering to avoid stale state.
  3. Treat it as expected in idempotent flows: catch FlowableIllegalStateException and verify the task was already completed.
  4. 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

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


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/11f1ee82d33736b2. Report an issue: GitHub.