flowable/flowable-engine · error · ActivitiException

Could not find cancel boundary event for cancel end event

Error message

Could not find cancel boundary event for cancel end event ${activity}

What it means

CancelEndEventActivityBehavior.execute() searches the activity's parent scopes for a cancel boundary event attached to the enclosing transaction subprocess. If none exists, the cancel end event was modeled outside a transaction subprocess with a compensating cancel boundary, so it throws ActivitiException.

Solutions

  1. Place the cancel end event inside a transaction subprocess and attach a cancel boundary event to that transaction
  2. Remove the cancel end event and use a plain end event if transaction rollback semantics are not intended
  3. Validate the BPMN model at deploy time to catch illegal cancel end event usage

Example fix

// before
<subProcess id="sp"><endEvent id="cancelEnd"><cancelEventDefinition/></endEvent></subProcess>
// after
<transaction id="tx">
  <endEvent id="cancelEnd"><cancelEventDefinition/></endEvent>
</transaction>
<boundaryEvent id="cancelBoundary" attachedToRef="tx"><cancelEventDefinition/></boundaryEvent>
Defensive patterns

Strategy: validation

Validate before calling

// Validate at model/deploy time: cancel end events must sit inside a transaction with a cancel boundary
boolean valid = hasTransactionSubprocessWithCancelBoundary(activityId);

Try / catch

try { /* execute flow */ }
catch (ActivitiException e) { if (e.getMessage().contains("cancel boundary event")) { flagModelingError(); } throw e; }

Prevention

When it happens

Trigger: A cancel end event is placed inside a subprocess that is not a transaction subprocess, or the transaction subprocess has no cancel boundary event attached, and execution reaches the cancel end event.

Common situations: Mis-modeled BPMN: cancel end event used outside transaction/cancel-boundary context; a model copied from another diagram where the boundary event was deleted.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/CancelEndEventActivityBehavior.java:38

import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.pvm.runtime.InterpretableExecution;
import org.flowable.engine.delegate.DelegateExecution;

/**
 * @author Daniel Meyer
 * @author Falko Menge
 */
public class CancelEndEventActivityBehavior extends FlowNodeActivityBehavior {

    @Override
    public void execute(DelegateExecution execution) {
        ActivityExecution activityExecution = (ActivityExecution) execution;
        // find cancel boundary event:
        ActivityImpl cancelBoundaryEvent = ScopeUtil
                .findInParentScopesByBehaviorType((ActivityImpl) activityExecution.getActivity(), CancelBoundaryEventActivityBehavior.class);

        if (cancelBoundaryEvent == null) {
            throw new ActivitiException("Could not find cancel boundary event for cancel end event " + activityExecution.getActivity());
        }

        ActivityExecution scopeExecution = ScopeUtil.findScopeExecutionForScope((ExecutionEntity) execution, cancelBoundaryEvent.getParentActivity());

        // end all executions and process instances in the scope of the transaction
        scopeExecution.destroyScope("cancel end event fired");

        // the scope execution executes the boundary event
        InterpretableExecution outgoingExecution = (InterpretableExecution) scopeExecution;
        outgoingExecution.setActivity(cancelBoundaryEvent);
        outgoingExecution.setActive(true);

        // execute the boundary
        cancelBoundaryEvent
                .getActivityBehavior()
                .execute(outgoingExecution);
    }

View on GitHub (pinned to d6d39ce1c6)