{"record":{"id":"e378040e2f4c26a7","repo":"flowable/flowable-engine","slug":"can-only-trigger-a-plan-item-that-is-in-the-active-e37804","errorCode":null,"errorMessage":"Can only trigger a plan item that is in the ACTIVE state","messagePattern":"Can only trigger a 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/ChildTaskActivityBehavior.java","lineNumber":77,"sourceCode":"    public void execute(CommandContext commandContext, PlanItemInstanceEntity planItemInstanceEntity) {\n        execute(commandContext, planItemInstanceEntity, null);\n    }\n\n    public abstract void execute(CommandContext commandContext, PlanItemInstanceEntity planItemInstanceEntity, VariableInfo variableInfo);\n\n    protected boolean evaluateIsBlocking(DelegatePlanItemInstance planItemInstance) {\n        boolean blocking = isBlocking;\n        if (StringUtils.isNotEmpty(isBlockingExpression)) {\n            Expression expression = CommandContextUtil.getExpressionManager().createExpression(isBlockingExpression);\n            blocking = (Boolean) expression.getValue(planItemInstance);\n        }\n        return blocking;\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 plan item that is in the ACTIVE state\");\n        }\n        CommandContextUtil.getAgenda(commandContext).planCompletePlanItemInstanceOperation(planItemInstance);\n    }\n\n    protected void handleInParameters(PlanItemInstanceEntity planItemInstanceEntity,\n                                      CmmnEngineConfiguration cmmnEngineConfiguration, Map<String, Object> inParametersMap,\n                                      ExpressionManager expressionManager) {\n\n        IOParameterUtil.processInParameters(inParameters, planItemInstanceEntity, inParametersMap, expressionManager);\n    }\n\n    protected String getBusinessKey(CmmnEngineConfiguration cmmnEngineConfiguration, PlanItemInstanceEntity planItemInstanceEntity, ChildTask childTask) {\n        String businessKey = null;\n        ExpressionManager expressionManager = cmmnEngineConfiguration.getExpressionManager();\n        CaseInstanceEntityManager caseInstanceEntityManager = cmmnEngineConfiguration.getCaseInstanceEntityManager();\n        if (!StringUtils.isEmpty(childTask.getBusinessKey())) {\n            Expression expression = expressionManager.createExpression(childTask.getBusinessKey());\n            businessKey = expression.getValue(planItemInstanceEntity).toString();","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/ChildTaskActivityBehavior.java#L59-L95","documentation":"ChildTaskActivityBehavior.trigger completes a child task plan item (used for generic child entity tasks). It only proceeds when the plan item instance is in the ACTIVE state; otherwise this error is thrown. After the state check it plans a CompletePlanItemInstanceOperation on the agenda.","triggerScenarios":"Triggering/completing a child task plan item instance whose state is not ACTIVE — e.g. it was already completed or terminated, or two concurrent completions raced.","commonSituations":"Double-completion from overlapping exit criteria and programmatic trigger; triggering a stale plan item id after the case moved on; concurrent workers completing the same child task.","solutions":["Query the plan item state and only trigger when it is ACTIVE.","Ensure only one completion path exists (either sentry-based exit or programmatic trigger, not both).","Re-fetch the plan item instance before triggering to avoid stale-state races.","Handle idempotency in your service layer so a second completion attempt is ignored."],"exampleFix":"// before\nruntimeService.triggerPlanItemInstance(childTaskPiiId); // may already be completed\n// after\nPlanItemInstance pii = runtimeService.createPlanItemInstanceQuery()\n    .planItemInstanceId(childTaskPiiId).singleResult();\nif (PlanItemInstanceState.ACTIVE.equals(pii.getState())) {\n    runtimeService.triggerPlanItemInstance(childTaskPiiId);\n}","handlingStrategy":"validation","validationCode":"PlanItemInstance pii = cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceId(id).singleResult();\nboolean canTrigger = pii != null && PlanItemInstanceState.ACTIVE.equals(pii.getState());","typeGuard":null,"tryCatchPattern":"try {\n    cmmnRuntimeService.triggerPlanItemInstance(id);\n} catch (FlowableIllegalStateException e) {\n    // not ACTIVE — treat as already completed; ignore if idempotent\n}","preventionTips":["Verify state == ACTIVE before triggering child tasks","Design completion flows with a single owner","Make retries idempotent","Re-fetch plan item state to close race windows"],"tags":["cmmn","child-task","state-machine","double-completion"],"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-14T11:17:12.474Z"}