flowable/flowable-engine · error · FlowableException
Following execution of sub process instance is not moved " +
Error message
Following execution of sub process instance is not moved " + subProcessExecution.getId()
What it means
Thrown during process instance migration when a subprocess of the migrating instance has an execution that is not included in the set of execution IDs being moved. Since the old sub process instance is about to be deleted, every active execution inside it must be part of the migration mapping; otherwise that execution's state would be silently lost, so the engine aborts.
Source
Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/dynamic/AbstractDynamicStateManager.java:721
//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);
ExecutionEntity parentExecution = executionEntityManager.findById(parentExecutionId);
if (parentExecution != null && parentExecution.getCurrentFlowElement() instanceof SubProcess parentSubProcess) {
if (!isSubProcessAncestorOfAnyNewFlowElements(parentSubProcess.getId(), moveToFlowElements)) {
ExecutionEntity toDeleteParentExecution = resolveParentExecutionToDelete(parentExecution, moveToFlowElements);View on GitHub (pinned to d6d39ce1c6)
Solutions
- Add an activity mapping for the unmapped execution's activity inside the subprocess
- Cancel the extra executions before migrating (terminate token/parallel branch) so all executions are covered
- Inspect execution tree (ExecutionQuery / act_ru_execution) to find all active executions and map each one
- Use autoMapActivities/autoMapEqualActivities to map identical activities across the two definitions instead of mapping manually
Example fix
// before
migrationBuilder.addActivityMapping("start", "start").migrateTo(newDefId);
// after
migrationBuilder.addActivityMapping("start", "start")
.addActivityMapping("parallelTaskB", "parallelTaskB") // cover subprocess branch
.autoMapEqualActivities()
.migrateTo(newDefId); Defensive patterns
Strategy: validation
Validate before calling
List<String> activeIds = runtimeService.createExecutionQuery().processInstanceId(pid).list()
.stream().map(Execution::getId).collect(toList());
List<String> unmapped = activeIds.stream().filter(id -> !executionIdsToMove.contains(id)).collect(toList());
if (!unmapped.isEmpty()) throw new IllegalStateException("Unmapped executions: " + unmapped); Try / catch
try {
migrationBuilder.migrateTo(newDefId);
} catch (FlowableException ex) {
if (ex.getMessage().startsWith("Following execution of sub process instance")) {
String execId = ex.getMessage().substring(ex.getMessage().lastIndexOf(' ') + 1);
// find its activity and add a mapping, then retry
} else throw ex;
} Prevention
- Enumerate all active executions inside subprocesses before migration
- Map parallel branches and multi-instance child executions explicitly
- Cancel stale parallel tokens before migrating
- Add integration tests migrating instances mid-subprocess
When it happens
Trigger: ProcessMigrationService migration where an active execution inside a subprocess (embedded or call-activity) corresponds to an activity not covered by addActivityMapping entries — e.g. a parallel branch or active wait state inside the subprocess that was not mapped.
Common situations: Migrating instances with parallel gateway branches inside subprocesses while only mapping the 'main' path; forgetting child executions of multi-instance subprocess activities; mapping only top-level activities.
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
- Unbound boundary event execution prevents the sub process in
- Execution '<execution>' is not a processInstance
- Error while completing sub process of execution ${processIns
- Could not find a scope execution for compensation boundary e
- completing() can only be called on a SubProcessActivityBehav
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/33b010628fa01983.
Report an issue: GitHub.