flowable/flowable-engine · error · FlowableException

Cannot add an entry criteria

Error message

Cannot add an entry criteria ${entryCriterion.getId()} no matching plan item found to attach it to

What it means

Thrown by ConversionHelper.addEntryCriterionToCurrentElement when an <entryCriterion> is encountered during conversion but there is no plan item (e.g. a stage, task, or event listener) currently on the stack to attach it to. Entry criteria must belong to a plan item, so a dangling criterion makes the document invalid.

Solutions

  1. Move the <entryCriterion> inside the correct <planItem> element so it has a plan item parent.
  2. Remove the orphaned entry criterion if no plan item should carry it.
  3. Validate the document against the CMMN XSD which enforces criterion placement inside planItem elements.

Example fix

<!-- before -->
<stage id="stage1">
  <entryCriterion id="crit1"/>
</stage>
<!-- after -->
<planItem id="task1">
  <entryCriterion id="crit1"/>
</planItem>
Defensive patterns

Strategy: validation

Validate before calling

// Ensure every entryCriterion is a child of a planItem before conversion
// e.g. XPath check:
// NodeList bad = (NodeList) xpath.evaluate("//entryCriterion[not(parent::planItem)]", doc, XPathConstants.NODESET);
// if (bad.getLength() > 0) throw new IllegalArgumentException("Entry criteria outside planItem");

Try / catch

try {
    cmmnModel = converter.convertToCmmnModel(stream, encoding);
} catch (FlowableException e) {
    if (e.getMessage().startsWith("Cannot add an entry criteria")) {
        // orphaned entry criterion: fix document structure
    }
    throw e;
}

Prevention

When it happens

Trigger: CMMN XML where an <entryCriterion> appears as a child of an element that is not a convertible plan item, or appears where the converter has no current hasEntryCriteria element (e.g. criterion placed directly in a stage instead of inside a planItem).

Common situations: Hand-written or third-party-generated CMMN placing entryCriterion outside <planItem>; copy-paste of criteria into the wrong parent; exporters emitting criteria for deleted plan items.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/ConversionHelper.java:126

        HasEntryCriteria hasEntryCriteria = null;
        while (hasEntryCriteria == null && iterator.hasPrevious()) {
            CmmnElement cmmnElement = iterator.previous();
            if (cmmnElement instanceof HasEntryCriteria) {
                hasEntryCriteria = (HasEntryCriteria) cmmnElement;
            }
        }
        if (hasEntryCriteria != null) {

            if (StringUtils.isEmpty(entryCriterion.getId())) {
                // An id is expected by the evaluation algorithm, so setting an internal one if there isn't one
                entryCriterion.setId(generateEntryCriterionId(hasEntryCriteria));
            }

            entryCriterion.setAttachedToRefId(hasEntryCriteria.getId());
            hasEntryCriteria.getEntryCriteria().add(entryCriterion);

        } else {
            throw new FlowableException("Cannot add an entry criteria " + entryCriterion.getId() + " no matching plan item found to attach it to");
        }
    }

    public void addExitCriterion(Criterion exitCriterion) {
        exitCriteria.add(exitCriterion);
    }

    public void addExitCriteriaToCurrentElement(Criterion exitCriterion) {
        addExitCriterion(exitCriterion);

        ListIterator<CmmnElement> iterator = currentCmmnElements.listIterator(currentCmmnElements.size());
        HasExitCriteria hasExitCriteria = null;
        while (hasExitCriteria == null && iterator.hasPrevious()) {
            CmmnElement cmmnElement = iterator.previous();
            if (cmmnElement instanceof HasExitCriteria) {
                hasExitCriteria = (HasExitCriteria) cmmnElement;
            }
        }

View on GitHub (pinned to d6d39ce1c6)