{"record":{"id":"11f1ee82d33736b2","repo":"flowable/flowable-engine","slug":"can-only-trigger-a-human-task-plan-item-that-is-in","errorCode":null,"errorMessage":"Can only trigger a human task plan item that is in the ACTIVE state","messagePattern":"Can only trigger a human task plan item that is in the ACTIVE state","errorType":"exception","errorClass":"FlowableIllegalStateException","httpStatus":null,"severity":"error","filePath":"modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/HumanTaskActivityBehavior.java","lineNumber":469,"sourceCode":"                }\n            }\n        }\n    }\n\n    private void handleTaskIdVariableStorage(PlanItemInstanceEntity planItemInstanceEntity, HumanTask humanTask, ExpressionManager expressionManager, TaskEntity taskEntity) {\n        if (StringUtils.isNotEmpty(humanTask.getTaskIdVariableName())) {\n            Expression expression = expressionManager.createExpression(humanTask.getTaskIdVariableName());\n            String idVariableName = (String) expression.getValue(planItemInstanceEntity);\n            if (StringUtils.isNotEmpty(idVariableName)) {\n                planItemInstanceEntity.setVariable(idVariableName, taskEntity.getId());\n            }\n        }\n    }\n\n    @Override\n    public void trigger(CommandContext commandContext, PlanItemInstanceEntity planItemInstance) {\n        if (!PlanItemInstanceState.ACTIVE.equals(planItemInstance.getState())) {\n            throw new FlowableIllegalStateException(\"Can only trigger a human task plan item that is in the ACTIVE state\");\n        }\n\n        CmmnEngineConfiguration cmmnEngineConfiguration = CommandContextUtil.getCmmnEngineConfiguration(commandContext);\n        TaskService taskService = cmmnEngineConfiguration.getTaskServiceConfiguration().getTaskService();\n        List<TaskEntity> taskEntities = taskService.findTasksBySubScopeIdScopeType(planItemInstance.getId(), ScopeTypes.CMMN);\n        if (taskEntities == null || taskEntities.isEmpty()) {\n            throw new FlowableException(\"No task entity found for \" + planItemInstance);\n        }\n\n        // Should be only one\n        for (TaskEntity taskEntity : taskEntities) {\n            if (!taskEntity.isDeleted()) {\n                TaskHelper.completeTask(taskEntity, taskEntity.getTempCompletedBy(), cmmnEngineConfiguration);\n            }\n        }\n\n        CommandContextUtil.getAgenda(commandContext).planCompletePlanItemInstanceOperation(planItemInstance);\n    }","sourceCodeStart":451,"sourceCodeEnd":487,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/HumanTaskActivityBehavior.java#L451-L487","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nruntimeService.triggerPlanItemInstance(planItemInstanceId);\n// after\nPlanItemInstance pii = cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceId(id).singleResult();\nif (pii != null && PlanItemInstanceState.ACTIVE.equals(pii.getState())) {\n    runtimeService.triggerPlanItemInstance(id);\n}","handlingStrategy":"validation","validationCode":"PlanItemInstance pii = cmmnRuntimeService.createPlanItemInstanceQuery()\n    .planItemInstanceInstanceId(id).singleResult();\nif (pii == null || !PlanItemInstanceState.ACTIVE.equals(pii.getState())) {\n    throw new IllegalStateException(\"Cannot trigger plan item \" + id + \" in state \" + (pii == null ? \"<missing>\" : pii.getState()));\n}","typeGuard":"boolean isTriggerable(PlanItemInstance pii) { return pii != null && PlanItemInstanceState.ACTIVE.equals(pii.getState()); }","tryCatchPattern":"try {\n    runtimeService.triggerPlanItemInstance(id);\n} catch (FlowableIllegalStateException e) {\n    if (e.getMessage().contains(\"Can only trigger a human task plan item\")) {\n        log.warn(\"Plan item {} already completed or not active; ignoring\", id);\n    }\n}","preventionTips":["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"],"tags":["cmmn","plan-item-state","invalid-state-transition","trigger"],"backgroundTag":"invalid-state-transition","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}