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
- Query plan item instances filtered to stages, e.g. planItemInstanceQuery.planItemInstanceDefinitionType("stage"), and use that id
- Verify the resolved plan item's isStage()/definition type before injecting
- 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
- Query with planItemInstanceDefinitionType("stage") to select only stages
- Do not assume case-level plan items are stages
- Unit-test injection code with a real stage id
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
- Can only complete plan item instances of type stage. Type is
- Can only complete a stage plan item instance that is marked
- Plan item instance for {eventSubscription} can not be found
- Cannot exit stage with 'complete' event type as the stage '{
- Could not find plan item instance for plan item with definit
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/1702fcaf36389230.
Report an issue: GitHub.