flowable/flowable-engine · error · FlowableException

Cannot find plan item '<planItemId>' in case definition with

Error message

Cannot find plan item '<planItemId>' in case definition with id '<caseDefinitionId>'

What it means

Flowable's CMMN dynamic state manager cannot resolve a plan item by its ID within the given case definition model. This is thrown when moving plan item instances to another state (dynamic case management) and the planItemId supplied in the change-state mapping does not exist in the (new) case definition. It protects against silently applying state changes to nonexistent elements.

Source

Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/AbstractCmmnDynamicStateManager.java:120

    protected CmmnEngineConfiguration cmmnEngineConfiguration;
    
    public AbstractCmmnDynamicStateManager(CmmnEngineConfiguration cmmnEngineConfiguration) {
        this.cmmnEngineConfiguration = cmmnEngineConfiguration;
    }

    protected PlanItem resolvePlanItemFromCmmnModelWithDefinitionId(String planItemDefinitionId, String caseDefinitionId) {
        CmmnModel cmmnModel = CaseDefinitionUtil.getCmmnModel(caseDefinitionId);
        PlanItem planItem = cmmnModel.findPlanItemByPlanItemDefinitionId(planItemDefinitionId);
        if (planItem == null) {
            throw new FlowableException("Cannot find plan item with definition id '" + planItemDefinitionId + "' in case definition with id '" + caseDefinitionId + "'");
        }
        return planItem;
    }

    protected PlanItem resolvePlanItemFromCmmnModel(CmmnModel cmmnModel, String planItemId, String caseDefinitionId) {
        PlanItem planItem = cmmnModel.findPlanItem(planItemId);
        if (planItem == null) {
            throw new FlowableException("Cannot find plan item '" + planItemId + "' in case definition with id '" + caseDefinitionId + "'");
        }
        return planItem;
    }

    protected void doMovePlanItemState(CaseInstanceChangeState caseInstanceChangeState, String originalCaseDefinitionId, CommandContext commandContext) {
        CaseInstanceEntityManager caseInstanceEntityManager = cmmnEngineConfiguration.getCaseInstanceEntityManager();
        CaseInstanceEntity caseInstance = caseInstanceEntityManager.findById(caseInstanceChangeState.getCaseInstanceId());
        
        Map<String, List<PlanItemInstanceEntity>> currentPlanItemInstances = retrievePlanItemInstances(caseInstanceChangeState.getCaseInstanceId());
        caseInstanceChangeState.setCurrentPlanItemInstances(currentPlanItemInstances);
        
        executeVerifySatisfiedSentryParts(caseInstanceChangeState, caseInstance, originalCaseDefinitionId, commandContext);
        
        executeTerminatePlanItemInstances(caseInstanceChangeState, caseInstance, commandContext);
        
        executeTerminateNonExistingPlanItemInstancesInTargetCmmnModel(caseInstanceChangeState, commandContext);
        
        setCaseDefinitionIdForPlanItemInstances(currentPlanItemInstances, caseInstanceChangeState.getCaseDefinitionToMigrateTo());

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Verify the planItemId exists in the target case definition XML (check the <planItem id="..."> attribute)
  2. Re-fetch the plan item ids from the current case definition version via the CMMN repository service / model instead of hard-coding them
  3. Check you are passing the plan item id, not the planItemDefinition or element id
  4. Redeploy the case definition if the referenced plan item was deleted

Example fix

// before
changeState.addPlanItemDefinitionMapping(
    new SimplePlanItemDefinitionMapping.Builder().planItemId("approveTaskOld").build());
// after
PlanItem planItem = cmmnModel.findPlanItem("approveTask"); // must exist in target case definition
changeState.addPlanItemDefinitionMapping(
    new SimplePlanItemDefinitionMapping.Builder().planItemId(planItem.getId()).build());
Defensive patterns

Strategy: validation

Validate before calling

PlanItem pi = cmmnModel.findPlanItem(planItemId);
if (pi == null) {
    throw new IllegalArgumentException("planItemId " + planItemId + " not present in case definition " + caseDefinitionId);
}

Type guard

boolean planItemExists(CmmnModel model, String planItemId) {
    return model != null && planItemId != null && model.findPlanItem(planItemId) != null;
}

Try / catch

try {
    cmmnRuntimeService.changePlanItemState(caseInstanceId, changeState);
} catch (FlowableException e) {
    if (e.getMessage().contains("Cannot find plan item")) {
        // re-resolve plan item ids from current case definition and retry
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling CaseInstanceService.changePlanItemState (or movePlanItemInstance(s)ToX) with a planItemDefinitionMapping/changeState whose planItemId is not present in the target case definition's CMMN model, e.g. a stale/typo'd element id or an id from a different case definition version.

Common situations: Hard-coded plan item ids that were renamed or removed after redeploying a new case definition version; copying ids from a different case model; using the plan item definition id instead of the plan item id.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


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