flowable/flowable-engine · error · FlowableException
Unbound boundary event execution prevents the sub process in
Error message
Unbound boundary event execution prevents the sub process instance to be moved " + subProcessExecution.getId()
What it means
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.
Source
Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/dynamic/AbstractDynamicStateManager.java:717
protected abstract boolean isDirectFlowElementExecutionMigration(FlowElement currentFlowElement, FlowElement newFlowElement);
protected void safeDeleteSubProcessInstance(String processInstanceId, List<ExecutionEntity> executionsPool, String deleteReason, CommandContext commandContext) {
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
//Confirm that all the subProcessExecutions are in the executions pool
List<ExecutionEntity> subProcessExecutions = executionEntityManager.findChildExecutionsByProcessInstanceId(processInstanceId);
Set<String> executionIdsToMove = new HashSet<>();
for (ExecutionEntity executionPoolItem : executionsPool) {
executionIdsToMove.add(executionPoolItem.getId());
}
for (ExecutionEntity subProcessExecution : subProcessExecutions) {
FlowElement currentFlowElement = subProcessExecution.getCurrentFlowElement();
if (currentFlowElement != null && currentFlowElement instanceof BoundaryEvent) {
String parentExecutionId = subProcessExecution.getParentId();
if (!StringUtils.isNotEmpty(parentExecutionId) || !executionIdsToMove.contains(parentExecutionId)) {
throw new FlowableException("Unbound boundary event execution prevents the sub process instance to be moved " + subProcessExecution.getId());
}
} else if (!executionIdsToMove.contains(subProcessExecution.getId())) {
throw new FlowableException("Following execution of sub process instance is not moved " + subProcessExecution.getId());
}
}
// delete the sub process instance
executionEntityManager.deleteProcessInstance(processInstanceId, deleteReason, true);
}
protected ExecutionEntity deleteParentExecutions(String parentExecutionId, Collection<FlowElementMoveEntry> moveToFlowElements, CommandContext commandContext) {
return deleteParentExecutions(parentExecutionId, moveToFlowElements, null, commandContext);
}
protected ExecutionEntity deleteParentExecutions(String parentExecutionId, Collection<FlowElementMoveEntry> moveToFlowElements, Collection<String> executionIdsNotToDelete, CommandContext commandContext) {
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
View on GitHub (pinned to d6d39ce1c6)
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)
Example fix
// before
migrationBuilder.migrateTo(newDefId);
// after
migrationBuilder.addActivityMapping("subProcessTask", "subProcessTask")
.addActivityMapping("boundaryUserTask", "boundaryUserTask")
.migrateTo(newDefId); Defensive patterns
Strategy: validation
Validate before calling
List<Execution> execs = runtimeService.createExecutionQuery().processInstanceId(pid).list();
boolean unbound = execs.stream().anyMatch(e ->
e.getCurrentFlowElement() instanceof BoundaryEvent &&
!mappedExecutionIds.contains(e.getParentId()));
if (unbound) throw new IllegalStateException("Map boundary-event parent executions before migrating"); Type guard
static boolean isMappedBoundary(ExecutionEntity e, Set<String> ids) {
return !(e.getCurrentFlowElement() instanceof BoundaryEvent) || ids.contains(e.getParentId());
} Try / catch
try {
migrationBuilder.migrateTo(newDefId);
} catch (FlowableException ex) {
if (ex.getMessage().startsWith("Unbound boundary event execution")) {
// augment activity mappings and retry
} else throw ex;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
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
- Following execution of sub process instance is not moved " +
- Error while completing sub process of execution ${processIns
- Could not resolve key for: ${eventDefinitionKey} in ${execut
- Could not find cancel boundary event for cancel end event in
- completing() can only be called on a SubProcessActivityBehav
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/5d47b5c40b8f5b24.
Report an issue: GitHub.