Activiti/Activiti · error · ActivitiException

No sub process execution found for cancel end event

Error message

No sub process execution found for cancel end event 

What it means

Thrown by CancelEndEventActivityBehavior.execute when a cancel end event cannot find the enclosing sub-process scope execution it must cancel. The behavior walks up the execution tree looking for the scope that contains a transaction, and if it never finds a parent scope execution it fails. Cancel end events are only valid inside a transactional sub-process with a cancel boundary event.

Solutions

  1. Move the cancel end event inside a <transaction subProcess> that has a cancel boundary event attached.
  2. Validate the BPMN model (activiti-modeler/validation) so cancel events only appear in transaction sub-processes.
  3. Check the execution tree in ACT_RU_EXECUTION for missing/damaged scope executions; restart the instance from a known-good state.
  4. Replace the cancel end event with an error end event + error boundary if transactional rollback semantics are not required.

Example fix

<!-- before -->
<subProcess id="sp"><endEvent id="e"><cancelEventDefinition/></endEvent></subProcess>
<!-- after -->
<transaction id="tx"><endEvent id="e"><cancelEventDefinition/></endEvent></transaction>
<boundaryEvent id="cancelB" attachedToRef="tx"><cancelEventDefinition/></boundaryEvent>
Defensive patterns

Strategy: validation

Validate before calling

// At deploy time: validate cancel end events are only inside <transaction> sub-processes
if (!"transaction".equals(parentElement.getType())) {
    throw new IllegalArgumentException("cancelEndEvent must be inside a transaction subProcess");
}

Try / catch

try {
    processEngine.getRuntimeService().signal(executionId);
} catch (ActivitiException e) {
    if (e.getMessage().startsWith("No sub process execution found")) {
        // model error: redeploy corrected definition
    }
}

Prevention

When it happens

Trigger: A <cancelEndEvent> is reached but the execution tree has no parent scope execution matching the transaction sub-process — e.g. cancel end event placed outside a transaction sub-process or model/config corruption removed the scope execution.

Common situations: BPMN modeling mistake: cancel end event in a regular (non-transaction) sub-process or at process level; historic process instances with modified executions after a DB migration or manual cleanup between Activiti versions.

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 Activiti/Activiti@56435b1a97 (2026-09-09). Data as JSON: /api/errors/1e6eba15ffafe833. Report an issue: GitHub.

Appendix: source

Thrown at activiti-core/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/CancelEndEventActivityBehavior.java:68

        ExecutionEntity currentlyExaminedExecution = executionEntityManager.findById(executionEntity.getParentId());
        while (currentlyExaminedExecution != null && parentScopeExecution == null) {
            if (currentlyExaminedExecution.getCurrentFlowElement() instanceof SubProcess) {
                parentScopeExecution = currentlyExaminedExecution;
                SubProcess subProcess = (SubProcess) currentlyExaminedExecution.getCurrentFlowElement();
                if (subProcess.getLoopCharacteristics() != null) {
                    ExecutionEntity miExecution = parentScopeExecution.getParent();
                    FlowElement miElement = miExecution.getCurrentFlowElement();
                    if (miElement != null && miElement.getId().equals(subProcess.getId())) {
                        parentScopeExecution = miExecution;
                    }
                }
            } else {
                currentlyExaminedExecution = executionEntityManager.findById(currentlyExaminedExecution.getParentId());
            }
        }

        if (parentScopeExecution == null) {
            throw new ActivitiException(
                "No sub process execution found for cancel end event " + executionEntity.getCurrentActivityId()
            );
        }

        SubProcess subProcess = (SubProcess) parentScopeExecution.getCurrentFlowElement();
        BoundaryEvent cancelBoundaryEvent = null;
        if (CollectionUtil.isNotEmpty(subProcess.getBoundaryEvents())) {
            for (BoundaryEvent boundaryEvent : subProcess.getBoundaryEvents()) {
                if (
                    CollectionUtil.isNotEmpty(boundaryEvent.getEventDefinitions()) &&
                    boundaryEvent.getEventDefinitions().getFirst() instanceof CancelEventDefinition
                ) {
                    cancelBoundaryEvent = boundaryEvent;
                    break;
                }
            }
        }

View on GitHub (pinned to 56435b1a97)