flowable/flowable-engine · warning

end event with cancelEventDefinition only supported inside…

Error message

end event with cancelEventDefinition only supported inside transaction subprocess (id={})

What it means

A cancel end event (cancelEventDefinition) is only legal inside an embedded transaction subprocess per the BPMN spec. When the parser finds one whose enclosing scope's 'type' property is not 'transaction', it logs this warning and skips wiring the CancelEndEventActivityBehavior, so the end event will not trigger cancellation at runtime.

Solutions

  1. Move the cancel end event into an embedded transaction subprocess (transaction subprocess element)
  2. Replace the cancel end event with a plain or error end event if cancellation semantics are not needed
  3. Add a boundary compensate/cancel event on the transaction subprocess if that was the intent

Example fix

// before
<process><endEvent id="end"><cancelEventDefinition/></endEvent></process>
// after
<process><subProcess triggeredByEvent="false"><transaction id="tx"><endEvent id="end"><cancelEventDefinition/></endEvent></transaction></subProcess></process>
Defensive patterns

Strategy: validation

Validate before calling

// ensure cancel end events sit inside a transaction subprocess
bpmnModel.getMainProcess().getFlowElementsOfType(EndEvent.class).forEach(e -> {
    if (hasCancelDefinition(e) && !isInsideTransactionSubprocess(bpmnModel, e)) {
        throw new IllegalArgumentException("cancel end event " + e.getId() + " not in transaction subprocess");
    }
});

Prevention

When it happens

Trigger: executeParse of an EndEvent carrying a CancelEventDefinition while bpmnParse.getCurrentScope().getProperty("type") is null or not "transaction" — i.e. the cancel end event sits in a plain subprocess, the top-level process, or a non-transactional container.

Common situations: Copying a cancel end event out of a transaction subprocess during refactoring; a modeler allowing cancel events anywhere; process templates missing the transactionSubprocess wrapper.

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/d8c4ee9dd7c5b175. Report an issue: GitHub.

Appendix: source

Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/parser/handler/EndEventParseHandler.java:66

        // Error end event
        if (eventDefinition instanceof org.flowable.bpmn.model.ErrorEventDefinition) {
            org.flowable.bpmn.model.ErrorEventDefinition errorDefinition = (org.flowable.bpmn.model.ErrorEventDefinition) eventDefinition;
            if (bpmnParse.getBpmnModel().containsErrorRef(errorDefinition.getErrorCode())) {
                String errorCode = bpmnParse.getBpmnModel().getErrors().get(errorDefinition.getErrorCode());
                if (StringUtils.isEmpty(errorCode)) {
                    LOGGER.warn("errorCode is required for an error event {}", endEvent.getId());
                }
                endEventActivity.setProperty("type", "errorEndEvent");
                errorDefinition.setErrorCode(errorCode);
            }
            endEventActivity.setActivityBehavior(bpmnParse.getActivityBehaviorFactory().createErrorEndEventActivityBehavior(endEvent, errorDefinition));

            // Cancel end event
        } else if (eventDefinition instanceof CancelEventDefinition) {
            ScopeImpl scope = bpmnParse.getCurrentScope();
            if (scope.getProperty("type") == null || !scope.getProperty("type").equals("transaction")) {
                LOGGER.warn("end event with cancelEventDefinition only supported inside transaction subprocess (id={})", endEvent.getId());
            } else {
                endEventActivity.setProperty("type", "cancelEndEvent");
                endEventActivity.setActivityBehavior(bpmnParse.getActivityBehaviorFactory().createCancelEndEventActivityBehavior(endEvent));
            }

            // Terminate end event
        } else if (eventDefinition instanceof TerminateEventDefinition) {
            endEventActivity.setAsync(endEvent.isAsynchronous());
            endEventActivity.setExclusive(!endEvent.isNotExclusive());
            endEventActivity.setActivityBehavior(bpmnParse.getActivityBehaviorFactory().createTerminateEndEventActivityBehavior(endEvent));

            // None end event
        } else if (eventDefinition == null) {
            endEventActivity.setAsync(endEvent.isAsynchronous());
            endEventActivity.setExclusive(!endEvent.isNotExclusive());
            endEventActivity.setActivityBehavior(bpmnParse.getActivityBehaviorFactory().createNoneEndEventActivityBehavior(endEvent));
        }
    }

View on GitHub (pinned to d6d39ce1c6)