flowable/flowable-engine · error · FlowableIllegalStateException

Can only trigger a plan item that is in the ACTIVE state

Error message

Can only trigger a plan item that is in the ACTIVE state

What it means

The engine attempted to trigger (complete) a CMMN plan item whose state is not ACTIVE. Only active plan items can be completed via the agenda's complete operation. This is an engine state invariant enforced in TaskActivityBehavior.trigger.

Source

Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/TaskActivityBehavior.java:57

    public void execute(CommandContext commandContext, PlanItemInstanceEntity planItemInstanceEntity) {
        if (!evaluateIsBlocking(planItemInstanceEntity)) {
            CommandContextUtil.getAgenda(commandContext).planCompletePlanItemInstanceOperation(planItemInstanceEntity);
        }
    }

    protected boolean evaluateIsBlocking(DelegatePlanItemInstance planItemInstance) {
        boolean blocking = isBlocking;
        if (StringUtils.isNotEmpty(isBlockingExpression)) {
            Expression expression = CommandContextUtil.getExpressionManager().createExpression(isBlockingExpression);
            blocking = (Boolean) expression.getValue(planItemInstance);
        }
        return blocking;
    }

    @Override
    public void trigger(CommandContext commandContext, PlanItemInstanceEntity planItemInstance) {
        if (!PlanItemInstanceState.ACTIVE.equals(planItemInstance.getState())) {
            throw new FlowableIllegalStateException("Can only trigger a plan item that is in the ACTIVE state");
        }
        CommandContextUtil.getAgenda(commandContext).planCompletePlanItemInstanceOperation(planItemInstance);
    }

}

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Check planItemInstance state (or query the plan item instance runtime) before triggering and only trigger when state == ACTIVE
  2. Guard against duplicate triggers (idempotency key, state re-check) in your calling code
  3. Use CaseService plan item lifecycle operations appropriate to the actual state rather than forcing complete

Example fix

// before
cmmnTaskService.triggerPlanItem(planItemInstance.getId());
// after
PlanItemInstance pii = cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceId(id).singleResult();
if (PlanItemInstanceState.ACTIVE.equals(pii.getState())) {
    cmmnTaskService.triggerPlanItem(id);
}
Defensive patterns

Strategy: try-catch

Validate before calling

const pii = cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceId(id).singleResult();
if (!pii || pii.getState() !== 'active') throw new Error('plan item not ACTIVE');

Type guard

function isActive(pii) { return pii != null && pii.getState() === 'active'; }

Try / catch

try {
  cmmnTaskService.triggerPlanItem(id);
} catch (e) {
  if (e instanceof FlowableIllegalStateException) { /* re-read state, skip or requeue */ }
  else throw e;
}

Prevention

When it happens

Trigger: Calling trigger on a task plan item that is available, waiting, suspended, completed, or terminated instead of active; racing completion via TaskService/CaseService while the plan item is in another lifecycle state.

Common situations: Programmatic completion from a listener or external trigger firing twice; manually completing a blocking task already acted upon; case instance suspended and later triggered.

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/3bb652d5266b29a9. Report an issue: GitHub.