{"record":{"id":"bbc1a8c04f2c04ab","repo":"flowable/flowable-engine","slug":"no-execution-found-for-sub-process-of-boundary-can","errorCode":null,"errorMessage":"No execution found for sub process of boundary cancel event ${boundaryEvent.getId()} for ${execution}","messagePattern":"No execution found for sub process of boundary cancel event (.+?) for (.+?)","errorType":"exception","errorClass":"FlowableException","httpStatus":null,"severity":"error","filePath":"modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/BoundaryCancelEventActivityBehavior.java","lineNumber":60,"sourceCode":"        BoundaryEvent boundaryEvent = (BoundaryEvent) execution.getCurrentFlowElement();\r\n\r\n        CommandContext commandContext = Context.getCommandContext();\r\n        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);\r\n        ExecutionEntityManager executionEntityManager = processEngineConfiguration.getExecutionEntityManager();\r\n\r\n        ExecutionEntity subProcessExecution = null;\r\n        // TODO: this can be optimized. A full search in the all executions shouldn't be needed\r\n        List<ExecutionEntity> processInstanceExecutions = executionEntityManager.findChildExecutionsByProcessInstanceId(execution.getProcessInstanceId());\r\n        for (ExecutionEntity childExecution : processInstanceExecutions) {\r\n            if (childExecution.getCurrentFlowElement() != null\r\n                    && childExecution.getCurrentFlowElement().getId().equals(boundaryEvent.getAttachedToRefId())) {\r\n                subProcessExecution = childExecution;\r\n                break;\r\n            }\r\n        }\r\n\r\n        if (subProcessExecution == null) {\r\n            throw new FlowableException(\"No execution found for sub process of boundary cancel event \" + boundaryEvent.getId() + \" for \" + execution);\r\n        }\r\n\r\n        EventSubscriptionService eventSubscriptionService = processEngineConfiguration.getEventSubscriptionServiceConfiguration().getEventSubscriptionService();\r\n        List<CompensateEventSubscriptionEntity> eventSubscriptions = eventSubscriptionService.findCompensateEventSubscriptionsByExecutionId(subProcessExecution.getParentId());\r\n\r\n        if (!eventSubscriptions.isEmpty()) {\r\n\r\n            String deleteReason = DeleteReason.BOUNDARY_EVENT_INTERRUPTING + \"(\" + boundaryEvent.getId() + \")\";\r\n\r\n            // cancel boundary is always sync\r\n            ScopeUtil.throwCompensationEvent(eventSubscriptions, execution, false);\r\n            executionEntityManager.deleteExecutionAndRelatedData(subProcessExecution, deleteReason, false);\r\n            if (subProcessExecution.getCurrentFlowElement() instanceof Activity activity) {\r\n                if (activity.getLoopCharacteristics() != null) {\r\n                    ExecutionEntity miExecution = subProcessExecution.getParent();\r\n                    List<ExecutionEntity> miChildExecutions = executionEntityManager.findChildExecutionsByParentExecutionId(miExecution.getId());\r\n                    for (ExecutionEntity miChildExecution : miChildExecutions) {\r\n                        if (!subProcessExecution.getId().equals(miChildExecution.getId()) && activity.getId().equals(miChildExecution.getCurrentActivityId())) {\r","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/BoundaryCancelEventActivityBehavior.java#L42-L78","documentation":"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).","triggerScenarios":"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).","commonSituations":"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.","solutions":["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.","Cancel/complete the subprocess through supported APIs (rollback via the transaction, RuntimeService APIs) instead of direct DB manipulation.","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).","If it occurs on a supported path, gather the execution-tree dump and report it as an engine bug (internal invariant violation)."],"exampleFix":"// before (external manipulation)\njdbcTemplate.update(\"delete from ACT_RU_EXECUTION where PARENT_ID_ = ?\", subProcessExecutionId);\n// after (use engine API to end the scope)\nmanagementService.executeCommand(new DeleteScopeCommand(subProcessExecutionId)); // or let the transaction rollback handle it","handlingStrategy":"try-catch","validationCode":"boolean childExists = runtimeService.createExecutionQuery()\n    .processInstanceId(processInstanceId)\n    .activityId(\"transactionalSubProcessId\")\n    .count() > 0;\nif (!childExists) {\n    throw new IllegalStateException(\"Sub process scope already gone; cannot fire cancel boundary\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    // engine-internal path; guard your own cancellation triggers\n    runtimeService.trigger(boundaryExecutionId);\n} catch (FlowableException e) {\n    if (e.getMessage().startsWith(\"No execution found for sub process of boundary cancel event\")) {\n        // execution tree inconsistent: audit DB changes / duplicate signals\n    } else {\n        throw e;\n    }\n}","preventionTips":["Never modify ACT_RU_EXECUTION (or other runtime tables) directly outside engine APIs","Avoid sending duplicate/late cancel signals to an already-terminated subprocess scope","Let transaction rollback propagate cancellation through the engine instead of manual cleanup","On unexplained occurrences, dump the execution tree and report as a possible engine bug"],"tags":["bpmn","transactional-subprocess","execution-tree","invariant"],"backgroundTag":"internal-invariant-violation","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}