flowable/flowable-engine · error · FlowableException

No execution found for sub process of boundary cancel event

Error message

No execution found for sub process of boundary cancel event ${boundaryEvent.getId()} for ${execution}

What it means

When a cancel boundary event on a transactional subprocess is triggered, BoundaryCancelEventActivityBehavior searches the execution tree for the subprocess's child execution. If no such child execution exists, the internal invariant of the transactional subprocess is broken, and the behavior throws FlowableException. This normally indicates an inconsistent execution tree (e.g. the subprocess scope already ended or the tree was manipulated externally).

Source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/BoundaryCancelEventActivityBehavior.java:60

        BoundaryEvent boundaryEvent = (BoundaryEvent) execution.getCurrentFlowElement();

        CommandContext commandContext = Context.getCommandContext();
        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
        ExecutionEntityManager executionEntityManager = processEngineConfiguration.getExecutionEntityManager();

        ExecutionEntity subProcessExecution = null;
        // TODO: this can be optimized. A full search in the all executions shouldn't be needed
        List<ExecutionEntity> processInstanceExecutions = executionEntityManager.findChildExecutionsByProcessInstanceId(execution.getProcessInstanceId());
        for (ExecutionEntity childExecution : processInstanceExecutions) {
            if (childExecution.getCurrentFlowElement() != null
                    && childExecution.getCurrentFlowElement().getId().equals(boundaryEvent.getAttachedToRefId())) {
                subProcessExecution = childExecution;
                break;
            }
        }

        if (subProcessExecution == null) {
            throw new FlowableException("No execution found for sub process of boundary cancel event " + boundaryEvent.getId() + " for " + execution);
        }

        EventSubscriptionService eventSubscriptionService = processEngineConfiguration.getEventSubscriptionServiceConfiguration().getEventSubscriptionService();
        List<CompensateEventSubscriptionEntity> eventSubscriptions = eventSubscriptionService.findCompensateEventSubscriptionsByExecutionId(subProcessExecution.getParentId());

        if (!eventSubscriptions.isEmpty()) {

            String deleteReason = DeleteReason.BOUNDARY_EVENT_INTERRUPTING + "(" + boundaryEvent.getId() + ")";

            // cancel boundary is always sync
            ScopeUtil.throwCompensationEvent(eventSubscriptions, execution, false);
            executionEntityManager.deleteExecutionAndRelatedData(subProcessExecution, deleteReason, false);
            if (subProcessExecution.getCurrentFlowElement() instanceof Activity activity) {
                if (activity.getLoopCharacteristics() != null) {
                    ExecutionEntity miExecution = subProcessExecution.getParent();
                    List<ExecutionEntity> miChildExecutions = executionEntityManager.findChildExecutionsByParentExecutionId(miExecution.getId());
                    for (ExecutionEntity miChildExecution : miChildExecutions) {
                        if (!subProcessExecution.getId().equals(miChildExecution.getId()) && activity.getId().equals(miChildExecution.getCurrentActivityId())) {

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Inspect ACT_RU_EXECUTION for the subprocess child executions; if rows were manually deleted, restore consistency by re-running the transaction or re-importing the process instance state.
  2. Cancel/complete the subprocess through supported APIs (rollback via the transaction, RuntimeService APIs) instead of direct DB manipulation.
  3. Reproduce the trigger sequence and guard against duplicate/late cancel signals (e.g. remove stale event subscriptions, avoid triggering the boundary event after the scope ended).
  4. If it occurs on a supported path, gather the execution-tree dump and report it as an engine bug (internal invariant violation).

Example fix

// before (external manipulation)
jdbcTemplate.update("delete from ACT_RU_EXECUTION where PARENT_ID_ = ?", subProcessExecutionId);
// after (use engine API to end the scope)
managementService.executeCommand(new DeleteScopeCommand(subProcessExecutionId)); // or let the transaction rollback handle it
Defensive patterns

Strategy: try-catch

Validate before calling

boolean childExists = runtimeService.createExecutionQuery()
    .processInstanceId(processInstanceId)
    .activityId("transactionalSubProcessId")
    .count() > 0;
if (!childExists) {
    throw new IllegalStateException("Sub process scope already gone; cannot fire cancel boundary");
}

Try / catch

try {
    // engine-internal path; guard your own cancellation triggers
    runtimeService.trigger(boundaryExecutionId);
} catch (FlowableException e) {
    if (e.getMessage().startsWith("No execution found for sub process of boundary cancel event")) {
        // execution tree inconsistent: audit DB changes / duplicate signals
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Cancelling a transaction subprocess (trigger() on the cancel boundary event / via transaction rollback propagation) when the subprocess's child execution is missing — e.g. the scope was already terminated, concurrent cancellation, or execution tree modified outside the engine APIs (direct DB edits, custom commands).

Common situations: Manually deleting/patching ACT_RU_EXECUTION rows; a custom JobHandler or command that removed the subprocess execution without destroying the boundary event subscription; racing cancel signals delivered twice; engine upgrades with legacy corrupted histories.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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