flowable/flowable-engine · error · FlowableException
Can only delete a child entity for a plan item with…
Error message
Can only delete a child entity for a plan item with reference type
What it means
deleteChildEntity cleans up the child entity (child process instance) of a plan item when the plan item is terminated or completed. If the plan item's referenceType is not PLAN_ITEM_CHILD_PROCESS, there is no child process to delete, and Flowable throws a FlowableException naming the expected reference type and plan item.
Solutions
- Only invoke deleteChildEntity for plan items with ReferenceTypes.PLAN_ITEM_CHILD_PROCESS; branch on referenceType before cleanup
- Ensure the plan item actually started its child process (reference set) before termination cleanup expects deletion
- Use the public CMMN runtime/termination APIs instead of calling behavior internals directly
- If a mixed model is expected, implement per-type child cleanup (case vs process) in the terminating code
Example fix
// before
deleteChildEntity(commandContext, planItemInstance); // throws for non-process children
// after
if (ReferenceTypes.PLAN_ITEM_CHILD_PROCESS.equals(planItemInstance.getReferenceType())) {
deleteChildEntity(commandContext, planItemInstance);
} else {
// other cleanup path (e.g. child case termination)
} Defensive patterns
Strategy: type-guard
Validate before calling
boolean deletable = planItemInstance.getReferenceType() != null
&& planItemInstance.getReferenceType().endsWith("childProcess"); Type guard
function canDeleteChild(pii) { return 'planItemChildProcess'.equalsIgnoreCase(pii.getReferenceType()); } Try / catch
try {
terminatePlanItem(planItem);
} catch (FlowableException e) {
if (e.getMessage().startsWith("Can only delete a child entity")) {
// fall back to type-appropriate cleanup
}
} Prevention
- Use public termination APIs instead of behavior internals
- Confirm the child process actually started before cleanup
- Implement per-type cleanup for mixed case models
- Check referenceType before every child-cleanup call
When it happens
Trigger: Case exit/termination path calls deleteChildEntity for a plan item whose referenceType is a child case or is unset — i.e. the delete routine was invoked for a non-process-task child relationship.
Common situations: Terminating a case containing mixed task types where generic cleanup code calls the process-task delete path; referenceType never set because the child process failed to start; custom termination logic invoking internals directly.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Can only delete a child entity for a plan item with…
- Cannot trigger case task plan item instance : reference type
- Cannot trigger process task plan item instance : reference…
- A dynamically created plan item can only be injected into a…
- A dynamically created plan item can only be injected into a…
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/c59f4a64ac331bbd.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/ProcessTaskActivityBehavior.java:216
@Override
public void deleteChildEntity(CommandContext commandContext, DelegatePlanItemInstance delegatePlanItemInstance, boolean cascade) {
if (ReferenceTypes.PLAN_ITEM_CHILD_PROCESS.equals(delegatePlanItemInstance.getReferenceType())) {
// This is not the regular termination through the agenda, but the historic plan item state still needs to be correct.
// The state needs to be set before deleting the process instance, because the process instance deletion triggers
// a ChildProcessInstanceStateChangeCallback which re-enters the CMMN engine to terminate the plan item.
// With the state already set to TERMINATED, this callback becomes a no-op and avoids re-triggering repetition rules.
PlanItemInstanceEntity planItemInstanceEntity = (PlanItemInstanceEntity) delegatePlanItemInstance;
if (!PlanItemInstanceState.TERMINATED.equals(planItemInstanceEntity.getState())) {
planItemInstanceEntity.setState(PlanItemInstanceState.TERMINATED);
planItemInstanceEntity.setEndedTime(CommandContextUtil.getCmmnEngineConfiguration(commandContext).getClock().getCurrentTime());
planItemInstanceEntity.setTerminatedTime(planItemInstanceEntity.getEndedTime());
CommandContextUtil.getCmmnHistoryManager(commandContext).recordPlanItemInstanceTerminated(planItemInstanceEntity);
}
deleteProcessInstance(commandContext, delegatePlanItemInstance);
} else {
throw new FlowableException("Can only delete a child entity for a plan item with reference type " + ReferenceTypes.PLAN_ITEM_CHILD_PROCESS + " for " + delegatePlanItemInstance);
}
}
protected void handleOutParameters(DelegatePlanItemInstance planItemInstance,
CaseInstanceEntity caseInstance,
ProcessInstanceService processInstanceService) {
if (outParameters == null) {
return;
}
for (IOParameter outParameter : outParameters) {
String variableName = null;
if (StringUtils.isNotEmpty(outParameter.getTarget())) {
variableName = outParameter.getTarget();
} else if (StringUtils.isNotEmpty(outParameter.getTargetExpression())) {View on GitHub (pinned to d6d39ce1c6)