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

  1. Only invoke deleteChildEntity for plan items with ReferenceTypes.PLAN_ITEM_CHILD_PROCESS; branch on referenceType before cleanup
  2. Ensure the plan item actually started its child process (reference set) before termination cleanup expects deletion
  3. Use the public CMMN runtime/termination APIs instead of calling behavior internals directly
  4. 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

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


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)