flowable/flowable-engine · error · FlowableObjectNotFoundException

plan item instance ${planItemInstanceId} doesn't exist

Error message

plan item instance ${planItemInstanceId} doesn't exist

What it means

After validating both arguments, GetPlanItemVariableInstanceCmd loads the plan item and throws FlowableObjectNotFoundException ('plan item instance <id> doesn't exist') when findById returns null. Unlike the null-argument errors, the inputs are well-formed but the referenced plan item is not present in the runtime tables.

Source

Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/cmd/GetPlanItemVariableInstanceCmd.java:50

    public GetPlanItemVariableInstanceCmd(String planItemInstanceId, String variableName) {
        this.planItemInstanceId = planItemInstanceId;
        this.variableName = variableName;
    }

    @Override
    public VariableInstance execute(CommandContext commandContext) {
        if (planItemInstanceId == null) {
            throw new FlowableIllegalArgumentException("planItemInstanceId is null");
        }
        if (variableName == null) {
            throw new FlowableIllegalArgumentException("variableName is null");
        }

        PlanItemInstanceEntity planItemInstance = CommandContextUtil.getPlanItemInstanceEntityManager(commandContext).findById(planItemInstanceId);

        if (planItemInstance == null) {
            throw new FlowableObjectNotFoundException("plan item instance " + planItemInstanceId + " doesn't exist", PlanItemInstance.class);
        }

        return planItemInstance.getVariableInstance(variableName, false);
    }
}

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Check existence first via createPlanItemInstanceQuery().planItemInstanceId(id).singleResult().
  2. If the plan item ended, use history services (HistoricVariableInstance queries) for past values.
  3. Re-fetch fresh ids from the live case instance before variable access.
  4. Confirm the caller and engine share the same database/schema.

Example fix

// before
VariableInstance vi = cmmnRuntimeService.getPlanItemVariableInstance(planItemId, varName);
// after
PlanItemInstance pii = cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceId(planItemId).singleResult();
VariableInstance vi = (pii != null) ? cmmnRuntimeService.getPlanItemVariableInstance(planItemId, varName) : null;
Defensive patterns

Strategy: try-catch

Validate before calling

PlanItemInstance pii = cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceId(planItemInstanceId).singleResult();
if (pii == null) return null;

Type guard

boolean planItemInstanceExists(String id) {
    return cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceId(id).count() > 0;
}

Try / catch

try {
    vi = cmmnRuntimeService.getPlanItemVariableInstance(planItemInstanceId, variableName);
} catch (FlowableObjectNotFoundException e) {
    log.warn("Plan item {} gone; falling back to history", planItemInstanceId);
    vi = fromHistory(planItemInstanceId, variableName);
}

Prevention

When it happens

Trigger: Reading a local variable for a plan item that already completed or whose case instance ended; using an id from a previous transaction/test run; querying the wrong engine database; passing a case or stage id instead of a plan item id.

Common situations: Variable listeners firing after completion race; cached ids in UI sessions pointing at terminated items; test fixtures replaying old ids; multi-environment deployments with divergent data.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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