flowable/flowable-engine · error · FlowableIllegalArgumentException

A dynamically created plan item can only be injected into a

Error message

A dynamically created plan item can only be injected into a running stage plan item.

What it means

After finding the plan item instance, CreateInjectedPlanItemInstanceCmd validates that it actually is a stage (PlanItemInstanceEntity.isStage()). Dynamically injected plan items can only be parented under a running stage; injecting into a task or milestone plan item instance is rejected with this FlowableIllegalArgumentException.

Source

Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/cmd/CreateInjectedPlanItemInstanceCmd.java:110

        // after adding the plan item to the stage, add it to the agenda for creation and afterwards for activation processing
        CmmnEngineAgenda agenda = CommandContextUtil.getAgenda(commandContext);
        agenda.planCreatePlanItemInstanceOperation(planItemInstanceEntity);
        agenda.planEvaluateToActivatePlanItemInstanceOperation(planItemInstanceEntity);

        return planItemInstanceEntity;
    }

    protected PlanItemInstanceEntity getStageInstanceEntity(CommandContext commandContext) {
        CmmnEngineConfiguration cmmnEngineConfiguration = CommandContextUtil.getCmmnEngineConfiguration(commandContext);
        PlanItemInstanceEntity planItemInstanceEntity = cmmnEngineConfiguration.getPlanItemInstanceEntityManager()
            .findById(planItemInstanceBuilder.getStagePlanItemInstanceId());

        if (planItemInstanceEntity == null) {
            throw new FlowableIllegalArgumentException(
                "The stage plan item instance id " + planItemInstanceBuilder.getStagePlanItemInstanceId() + " could not be found or is no longer active.");
        }
        if (!planItemInstanceEntity.isStage()) {
            throw new FlowableIllegalArgumentException("A dynamically created plan item can only be injected into a running stage plan item.");
        }
        return planItemInstanceEntity;
    }

    protected CaseInstanceEntity getCaseInstanceEntity(CommandContext commandContext) {
        CmmnEngineConfiguration cmmnEngineConfiguration = CommandContextUtil.getCmmnEngineConfiguration(commandContext);
        CaseInstanceEntity caseInstanceEntity = cmmnEngineConfiguration.getCaseInstanceEntityManager().findById(planItemInstanceBuilder.getCaseInstanceId());
        if (caseInstanceEntity == null) {
            throw new FlowableIllegalArgumentException(
                "The case instance with id " + planItemInstanceBuilder.getCaseInstanceId() + " could not be found or is no longer an active case instance.");
        }
        return caseInstanceEntity;
    }
}

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Query plan item instances filtered to stages, e.g. planItemInstanceQuery.planItemInstanceDefinitionType("stage"), and use that id
  2. Verify the resolved plan item's isStage()/definition type before injecting
  3. If the target should be the case itself, check whether the API requires a case-level injection variant instead of stage injection

Example fix

// before
builder.stagePlanItemInstanceId(anyPlanItem.getId()).inject();
// after
if (anyPlanItem.getPlanItemDefinitionType().equals("stage")) {
    builder.stagePlanItemInstanceId(anyPlanItem.getId()).inject();
}
Defensive patterns

Strategy: validation

Validate before calling

PlanItemInstance pi = cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceId(id).singleResult();
if (pi == null || !"stage".equals(pi.getPlanItemDefinitionType())) throw new IllegalArgumentException("target must be a stage");

Type guard

boolean isStage(PlanItemInstance pi) { return pi != null && "stage".equals(pi.getPlanItemDefinitionType()); }

Try / catch

try { builder.stagePlanItemInstanceId(id).inject(); }
catch (FlowableIllegalArgumentException e) { if (e.getMessage().contains("only be injected into a running stage")) { /* pick a stage id instead */ } else throw e; }

Prevention

When it happens

Trigger: Calling the injected plan item builder with stagePlanItemInstanceId set to the id of a non-stage plan item instance (task, user event listener, milestone, or case-level plan item) instead of a stage.

Common situations: Selecting an id from a plan item query without filtering on element type; assuming the root/case plan item can host injected items; misreading a child task id as its parent stage id.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/1702fcaf36389230. Report an issue: GitHub.