{"record":{"id":"7511fa1bbcf55bed","repo":"flowable/flowable-engine","slug":"can-only-trigger-a-plan-item-that-is-in-the-active-7511fa","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/ProcessTaskActivityBehavior.java","lineNumber":163,"sourceCode":"                processInstanceService.deleteProcessInstance(processInstanceId);\n                FaultPropagation.propagateFault(businessError, commandContext, planItemInstanceEntity);\n                return;\n            }\n\n        } else {\n            processInstanceService.startProcessInstance(processDefinitionId, processInstanceId, planItemInstanceEntity.getStageInstanceId(),\n                    planItemInstanceEntity.getTenantId(), inParametersMap, businessKey, variableFormVariables, variableFormInfo, variableFormOutcome);\n        }\n\n        if (!blocking) {\n            CommandContextUtil.getAgenda(commandContext).planCompletePlanItemInstanceOperation(planItemInstanceEntity);\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 plan item that is in the ACTIVE state\");\n        }\n        if (planItemInstance.getReferenceId() == null) {\n            throw new FlowableIllegalStateException(\"Cannot trigger process task plan item instance : no reference id set\");\n        }\n        if (!ReferenceTypes.PLAN_ITEM_CHILD_PROCESS.equals(planItemInstance.getReferenceType())) {\n            throw new FlowableException(\"Cannot trigger process task plan item instance : reference type '\"\n                    + planItemInstance.getReferenceType() + \"' not supported for \" + planItemInstance);\n        }\n\n        // Need to be set before planning the complete operation\n        CmmnEngineConfiguration cmmnEngineConfiguration = CommandContextUtil.getCmmnEngineConfiguration(commandContext);\n        CaseInstanceEntity caseInstance = cmmnEngineConfiguration.getCaseInstanceEntityManager().findById(planItemInstance.getCaseInstanceId());\n        handleOutParameters(planItemInstance, caseInstance, cmmnEngineConfiguration.getProcessInstanceService());\n\n        // Triggering the plan item (as opposed to a regular complete) terminates the process instance\n        CommandContextUtil.getAgenda(commandContext).planCompletePlanItemInstanceOperation(planItemInstance);\n        deleteProcessInstance(commandContext, planItemInstance);\n    }","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/ProcessTaskActivityBehavior.java#L145-L181","documentation":"ProcessTaskActivityBehavior.trigger() is invoked to complete/notify a process task plan item, but the plan item is not in the ACTIVE state, so triggering it is illegal. Flowable throws FlowableIllegalStateException to protect the state machine of the case execution.","triggerScenarios":"Calling CmmnRuntimeService/PlanItemInstance trigger (e.g. completePlanItemInstance or a child-process completion callback) on a plan item whose state is AVAILABLE, ENABLED, COMPLETED, TERMINATED, etc.","commonSituations":"Race condition where the child process finished after the case was already terminated or the plan item completed; double-completion from an event/callback listener; manually triggering an async process task that has not yet become active.","solutions":["Check planItemInstance.getState() equals PlanItemInstanceState.ACTIVE before calling trigger/completion APIs","Guard the child-process completion callback so it is idempotent and skips plan items already completed/terminated","Use CmmnRuntimeService.createPlanItemInstanceQuery() to fetch the current state from the DB rather than a stale in-memory object","If termination races are expected, catch FlowableIllegalStateException around the trigger call and treat it as already-finalized"],"exampleFix":"// before\nruntimeService.completePlanItemInstance(planItemId); // may throw if not ACTIVE\n// after\nPlanItemInstance pii = runtimeService.createPlanItemInstanceQuery().planItemInstanceId(planItemId).singleResult();\nif (pii != null && PlanItemInstanceState.ACTIVE.equals(pii.getState())) {\n    runtimeService.completePlanItemInstance(planItemId);\n}","handlingStrategy":"type-guard","validationCode":"PlanItemInstance pii = runtimeService.createPlanItemInstanceQuery()\n    .planItemInstanceId(planItemId).singleResult();\nboolean triggerable = pii != null && \"active\".equals(pii.getState());","typeGuard":"function isActive(pii) { return pii != null && 'active' === pii.getState(); }","tryCatchPattern":"try {\n    runtimeService.completePlanItemInstance(planItemId);\n} catch (FlowableIllegalStateException e) {\n    // plan item already finalized (completed/terminated); treat as idempotent no-op\n}","preventionTips":["Fetch fresh state from runtime service before triggering","Make completion callbacks idempotent","Avoid triggering async process tasks manually","Watch for termination races between case exit and child completion"],"tags":["cmmn","illegal-state","plan-item","lifecycle"],"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"}