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

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.

Source

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

    public void execute(CommandContext commandContext, PlanItemInstanceEntity planItemInstanceEntity) {
        execute(commandContext, planItemInstanceEntity, null);
    }

    public abstract void execute(CommandContext commandContext, PlanItemInstanceEntity planItemInstanceEntity, VariableInfo variableInfo);

    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);
    }

    protected void handleInParameters(PlanItemInstanceEntity planItemInstanceEntity,
                                      CmmnEngineConfiguration cmmnEngineConfiguration, Map<String, Object> inParametersMap,
                                      ExpressionManager expressionManager) {

        IOParameterUtil.processInParameters(inParameters, planItemInstanceEntity, inParametersMap, expressionManager);
    }

    protected String getBusinessKey(CmmnEngineConfiguration cmmnEngineConfiguration, PlanItemInstanceEntity planItemInstanceEntity, ChildTask childTask) {
        String businessKey = null;
        ExpressionManager expressionManager = cmmnEngineConfiguration.getExpressionManager();
        CaseInstanceEntityManager caseInstanceEntityManager = cmmnEngineConfiguration.getCaseInstanceEntityManager();
        if (!StringUtils.isEmpty(childTask.getBusinessKey())) {
            Expression expression = expressionManager.createExpression(childTask.getBusinessKey());
            businessKey = expression.getValue(planItemInstanceEntity).toString();

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Query the plan item state and only trigger when it is ACTIVE.
  2. Ensure only one completion path exists (either sentry-based exit or programmatic trigger, not both).
  3. Re-fetch the plan item instance before triggering to avoid stale-state races.
  4. Handle idempotency in your service layer so a second completion attempt is ignored.

Example fix

// before
runtimeService.triggerPlanItemInstance(childTaskPiiId); // may already be completed
// after
PlanItemInstance pii = runtimeService.createPlanItemInstanceQuery()
    .planItemInstanceId(childTaskPiiId).singleResult();
if (PlanItemInstanceState.ACTIVE.equals(pii.getState())) {
    runtimeService.triggerPlanItemInstance(childTaskPiiId);
}
Defensive patterns

Strategy: validation

Validate before calling

PlanItemInstance pii = cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceId(id).singleResult();
boolean canTrigger = pii != null && PlanItemInstanceState.ACTIVE.equals(pii.getState());

Try / catch

try {
    cmmnRuntimeService.triggerPlanItemInstance(id);
} catch (FlowableIllegalStateException e) {
    // not ACTIVE — treat as already completed; ignore if idempotent
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

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