flowable/flowable-engine · error · FlowableIllegalArgumentException
The case element needs to be a plan item, but is a
Error message
The case element needs to be a plan item, but is a
What it means
Thrown when the resolved case element exists but is not a PlanItem instance (e.g. it is a Stage, Milestone, or CasePageTask-level element). Dynamic plan item injection only supports creating new plan items; the elementId must point at a <planItem> in the case model.
Solutions
- Verify the element is a plan item: if (element instanceof PlanItem) before injecting, otherwise pick the correct planItem id from the model.
- Use cmmnModel's getPlanItems()/stage plan items to enumerate only plan item ids.
- If you meant to trigger a milestone, use the plan item runtime service triggerPlanItemInstance API instead.
Example fix
// before
CaseElement el = caseModel.getAllCaseElements().get(elementId);
planItemInstanceBuilder.elementId(elementId); // throws if el is a Stage/Milestone
// after
CaseElement el = caseModel.getAllCaseElements().get(elementId);
if (el instanceof PlanItem) {
planItemInstanceBuilder.elementId(elementId);
} else {
throw new IllegalStateException(elementId + " is not a plan item");
} Defensive patterns
Strategy: validation
Validate before calling
Case caseModel = cmmnRepositoryService.getCmmnModel(caseDefinitionId).getCases().get(0);
CaseElement el = caseModel.getAllCaseElements().get(elementId);
if (!(el instanceof PlanItem)) {
throw new IllegalArgumentException("elementId must reference a planItem, got " + el.getClass().getSimpleName());
}
planItemInstanceBuilder.elementId(elementId); Type guard
boolean isInjectablePlanItem(CaseElement el) {
return el instanceof PlanItem;
} Prevention
- Only enumerate stage.getPlanItems() when choosing dynamic element ids.
- Do not pass stage/milestone ids to dynamic plan item builders.
When it happens
Trigger: Calling planItemInstanceBuilder.elementId(id) where id resolves to a case element that is not a plan item, such as passing a stage id or milestone id from allCaseElements().
Common situations: Iterating allCaseElements() and picking the first matching id without checking its type; confusing the plan item's id with the underlying planItemDefinition/task id; passing a milestone id intending to trigger it (which requires a different API).
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
- Could not find case element with id
- Could not find case model with case definition id
- A dynamically created plan item can only be injected into a…
- A dynamically created plan item can only be injected into a…
- Can only complete a stage plan item instance that is marked…
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/90987aded44a523e.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/cmd/CreateInjectedPlanItemInstanceCmd.java:57
public CreateInjectedPlanItemInstanceCmd(InjectedPlanItemInstanceBuilderImpl planItemInstanceBuilder) {
this.planItemInstanceBuilder = planItemInstanceBuilder;
}
@Override
public PlanItemInstance execute(CommandContext commandContext) {
Case caseModel = CaseDefinitionUtil.getCase(planItemInstanceBuilder.getCaseDefinitionId());
if (caseModel == null) {
throw new FlowableIllegalArgumentException("Could not find case model with case definition id " + planItemInstanceBuilder.getCaseDefinitionId());
}
CaseElement caseElement = caseModel.getAllCaseElements().get(planItemInstanceBuilder.getElementId());
if (caseElement == null) {
throw new FlowableIllegalArgumentException("Could not find case element with id " + planItemInstanceBuilder.getElementId());
}
if (!(caseElement instanceof PlanItem)) {
throw new FlowableIllegalArgumentException("The case element needs to be a plan item, but is a " + caseElement.getClass().getName());
}
String runningCaseDefinitionId;
String caseInstanceId;
String tenantId;
PlanItemInstance stagePlanItemInstance = null;
if (planItemInstanceBuilder.injectInStage()) {
stagePlanItemInstance = getStageInstanceEntity(commandContext);
caseInstanceId = stagePlanItemInstance.getCaseInstanceId();
tenantId = stagePlanItemInstance.getTenantId();
runningCaseDefinitionId = stagePlanItemInstance.getCaseDefinitionId();
} else if (planItemInstanceBuilder.injectInCase()) {
CaseInstance caseInstance = getCaseInstanceEntity(commandContext);
caseInstanceId = caseInstance.getId();
tenantId = caseInstance.getTenantId();
runningCaseDefinitionId = caseInstance.getCaseDefinitionId();
} else {View on GitHub (pinned to d6d39ce1c6)