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

  1. Add an activity mapping for the unmapped execution's activity inside the subprocess
  2. Cancel the extra executions before migrating (terminate token/parallel branch) so all executions are covered
  3. Inspect execution tree (ExecutionQuery / act_ru_execution) to find all active executions and map each one
  4. 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

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


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