{"record":{"id":"5d47b5c40b8f5b24","repo":"flowable/flowable-engine","slug":"unbound-boundary-event-execution-prevents-the-sub","errorCode":null,"errorMessage":"Unbound boundary event execution prevents the sub process instance to be moved \" + subProcessExecution.getId()","messagePattern":"Unbound boundary event execution prevents the sub process instance to be moved \" \\+ subProcessExecution\\.getId\\(\\)","errorType":"exception","errorClass":"FlowableException","httpStatus":null,"severity":"error","filePath":"modules/flowable-engine/src/main/java/org/flowable/engine/impl/dynamic/AbstractDynamicStateManager.java","lineNumber":717,"sourceCode":"    protected abstract boolean isDirectFlowElementExecutionMigration(FlowElement currentFlowElement, FlowElement newFlowElement);\n\n    protected void safeDeleteSubProcessInstance(String processInstanceId, List<ExecutionEntity> executionsPool, String deleteReason, CommandContext commandContext) {\n        ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);\n\n        //Confirm that all the subProcessExecutions are in the executions pool\n        List<ExecutionEntity> subProcessExecutions = executionEntityManager.findChildExecutionsByProcessInstanceId(processInstanceId);\n        \n        Set<String> executionIdsToMove = new HashSet<>();\n        for (ExecutionEntity executionPoolItem : executionsPool) {\n            executionIdsToMove.add(executionPoolItem.getId());\n        }\n\n        for (ExecutionEntity subProcessExecution : subProcessExecutions) {\n            FlowElement currentFlowElement = subProcessExecution.getCurrentFlowElement();\n            if (currentFlowElement != null && currentFlowElement instanceof BoundaryEvent) {\n                String parentExecutionId = subProcessExecution.getParentId();\n                if (!StringUtils.isNotEmpty(parentExecutionId) || !executionIdsToMove.contains(parentExecutionId)) {\n                    throw new FlowableException(\"Unbound boundary event execution prevents the sub process instance to be moved \" + subProcessExecution.getId());\n                }\n            \n            } else if (!executionIdsToMove.contains(subProcessExecution.getId())) {\n                throw new FlowableException(\"Following execution of sub process instance is not moved \" + subProcessExecution.getId());\n            }\n        }\n\n        // delete the sub process instance\n        executionEntityManager.deleteProcessInstance(processInstanceId, deleteReason, true);\n    }\n\n    protected ExecutionEntity deleteParentExecutions(String parentExecutionId, Collection<FlowElementMoveEntry> moveToFlowElements, CommandContext commandContext) {\n        return deleteParentExecutions(parentExecutionId, moveToFlowElements, null, commandContext);\n    }\n\n    protected ExecutionEntity deleteParentExecutions(String parentExecutionId, Collection<FlowElementMoveEntry> moveToFlowElements, Collection<String> executionIdsNotToDelete, CommandContext commandContext) {\n        ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);\n","sourceCodeStart":699,"sourceCodeEnd":735,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine/src/main/java/org/flowable/engine/impl/dynamic/AbstractDynamicStateManager.java#L699-L735","documentation":"Thrown by AbstractDynamicStateManager when moving a process instance to a different process definition (process instance migration/upgrade). Before deleting the old sub process instance executions, the engine validates that every execution sitting on a BoundaryEvent has its parent execution included in the set of execution IDs being moved. If a boundary-event execution is 'unbound' (no parent, or parent not part of the migration set), the migration would orphan it, so the engine refuses.","triggerScenarios":"Calling ProcessInstanceMigrationBuilder.migrateTo(processDefinitionId) / ProcessMigrationService process-instance migration while a subprocess in the instance has an active boundary-event execution whose parent execution is not mapped in the migration activity mapping.","commonSituations":"Migrating instances that are waiting on a boundary timer/message/signal inside an embedded or call-activity subprocess without mapping the boundary event or its parent activity in the migration map; partial activity mappings that miss subprocess-internal executions.","solutions":["Add the boundary event's activity (and its parent subprocess activity) to the migration activity mapping, e.g. .addActivityMapping(\"oldBoundaryTask\", \"newBoundaryTask\")","Map the parent subprocess execution so executionIdsToMove contains the parent of the boundary-event execution","Enable a mapped boundary event in the target definition so the execution can be moved, or cancel/complete the boundary wait state before migrating","Migrate in a state where no boundary-event executions are active (e.g. after timers fire or messages arrive)"],"exampleFix":"// before\nmigrationBuilder.migrateTo(newDefId);\n// after\nmigrationBuilder.addActivityMapping(\"subProcessTask\", \"subProcessTask\")\n    .addActivityMapping(\"boundaryUserTask\", \"boundaryUserTask\")\n    .migrateTo(newDefId);","handlingStrategy":"validation","validationCode":"List<Execution> execs = runtimeService.createExecutionQuery().processInstanceId(pid).list();\nboolean unbound = execs.stream().anyMatch(e ->\n    e.getCurrentFlowElement() instanceof BoundaryEvent &&\n    !mappedExecutionIds.contains(e.getParentId()));\nif (unbound) throw new IllegalStateException(\"Map boundary-event parent executions before migrating\");","typeGuard":"static boolean isMappedBoundary(ExecutionEntity e, Set<String> ids) {\n    return !(e.getCurrentFlowElement() instanceof BoundaryEvent) || ids.contains(e.getParentId());\n}","tryCatchPattern":"try {\n    migrationBuilder.migrateTo(newDefId);\n} catch (FlowableException ex) {\n    if (ex.getMessage().startsWith(\"Unbound boundary event execution\")) {\n        // augment activity mappings and retry\n    } else throw ex;\n}","preventionTips":["Map every boundary event activity in the target definition before migrating","Inspect act_ru_execution / ExecutionQuery for boundary-event waits pre-migration","Prefer autoMapEqualActivities to cover identical subprocess activities","Test migration on a staging copy of the instance data"],"tags":["process-migration","boundary-event","flowable","subprocess"],"backgroundTag":"invalid-state-transition","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"}