{"record":{"id":"36340be90b57b45a","repo":"flowable/flowable-engine","slug":"can-only-trigger-a-plan-item-that-is-in-the-active","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/agenda/operation/TriggerPlanItemInstanceOperation.java","lineNumber":47,"sourceCode":"    \n    public TriggerPlanItemInstanceOperation(CommandContext commandContext, PlanItemInstanceEntity planItemInstanceEntity) {\n        super(commandContext, planItemInstanceEntity);\n    }\n    \n    @Override\n    public void run() {\n        if (PlanItemInstanceState.ACTIVE.equals(planItemInstanceEntity.getState())\n                || (planItemInstanceEntity.getPlanItem() != null \n                && planItemInstanceEntity.getPlanItem().getPlanItemDefinition() instanceof EventListener\n                && PlanItemInstanceState.AVAILABLE.equals(planItemInstanceEntity.getState()))){\n            \n            executeTrigger();\n\n        } else {\n            if (planItemInstanceEntity.getPlanItem() != null && planItemInstanceEntity.getPlanItem().getPlanItemDefinition() instanceof EventListener){\n                throw new FlowableIllegalStateException(\"Can only trigger an event listener plan item that is in the AVAILABLE state\");\n            } else {\n                throw new FlowableIllegalStateException(\"Can only trigger a plan item that is in the ACTIVE state\");\n            }\n\n        }\n    }\n\n    protected void executeTrigger() {\n        Object behaviorObject = planItemInstanceEntity.getPlanItem().getBehavior();\n        if (!(behaviorObject instanceof CmmnTriggerableActivityBehavior)) {\n            throw new FlowableException(\"Cannot trigger a plan item which activity behavior does not implement the \" \n                    + CmmnTriggerableActivityBehavior.class + \" interface in \" + planItemInstanceEntity);\n        }\n        CmmnTriggerableActivityBehavior behavior = (CmmnTriggerableActivityBehavior) planItemInstanceEntity.getPlanItem().getBehavior();\n        if (behavior instanceof CoreCmmnTriggerableActivityBehavior) {\n            ((CoreCmmnTriggerableActivityBehavior) behavior).trigger(commandContext, planItemInstanceEntity);\n        } else {\n            behavior.trigger(planItemInstanceEntity);\n        }\n    }","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/agenda/operation/TriggerPlanItemInstanceOperation.java#L29-L65","documentation":"TriggerPlanItemInstanceOperation moves a plan item instance forward only when its state is ACTIVE. If it is not, Flowable checks whether the plan item is an EventListener and throws a slightly different message; otherwise it throws this generic state error. It guards the CMMN lifecycle so only active (running) plan items can be triggered.","triggerScenarios":"Calling PlanItemInstanceRuntimeService.triggerPlanItemInstance(planItemInstanceId) for a plan item whose state is not ACTIVE (e.g. AVAILABLE, COMPLETED, TERMINATED); or an agenda operation invoking run() on a stale/non-active plan item instance.","commonSituations":"Triggering an already-completed human task or milestone; triggering an event-listener plan item (gets the sibling AVAILABLE-state message instead); stale references to a plan item id from a previous case execution; retrying a trigger after the case moved on.","solutions":["Query the plan item instance state via PlanItemInstanceQuery and only trigger when state equals ACTIVE.","If the plan item is an event listener, use the appropriate event-trigger API (it must be AVAILABLE, not ACTIVE).","Re-fetch the current plan item instance id instead of reusing a cached/stale id.","Register a plan item listener or use case stage transitions instead of manual triggering for non-active items."],"exampleFix":"// before\nruntimeService.triggerPlanItemInstance(planItemId); // throws if not ACTIVE\n// after\nPlanItemInstance pii = runtimeService.createPlanItemInstanceQuery()\n    .planItemInstanceId(planItemId).singleResult();\nif (PlanItemInstanceState.ACTIVE.equals(pii.getState())) {\n    runtimeService.triggerPlanItemInstance(planItemId);\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    // re-query state; plan item is not ACTIVE — skip or log\n}","preventionTips":["Always query the plan item state before triggering","Never cache plan item ids across case lifecycle changes","Use event listeners' dedicated trigger APIs instead of generic trigger","Centralize trigger calls in a service that checks state"],"tags":["cmmn","state-machine","plan-item","flowable"],"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"}