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
- Move the cancel end event into an embedded transaction subprocess (transaction subprocess element)
- Replace the cancel end event with a plain or error end event if cancellation semantics are not needed
- 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
- Only use cancel end events inside <transaction> subprocess elements
- Model transactional work as transaction subprocesses with boundary compensate handlers
- Validate the process with BpmnParse/BpmnModel validation before deployment
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
- Activity needed for multi instance cannot bv found
- Could not find cancel boundary event for cancel end event
- Error parsing XML
- Error parsing XML
- Error while parsing BPMN model.
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)