flowable/flowable-engine · error · FlowableException
Multiple multi instance executions found for activity id
Error message
Multiple multi instance executions found for activity id
What it means
Thrown by AddMultiInstanceExecutionCmd's recursive searchForMultiInstanceActivity when more than one child execution matches the given activityId and is flagged as a multi-instance root. The dynamic-addition API requires exactly one MI root execution per activity id; finding two means the execution tree is ambiguous or corrupted, so the engine refuses to guess which MI root to add the new instance to.
Source
Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/cmd/AddMultiInstanceExecutionCmd.java:97
if (!multiInstanceLoopCharacteristics.isSequential()) {
miExecution.setActive(true);
miExecution.setScope(false);
childExecution.setCurrentFlowElement(miActivityElement);
CommandContextUtil.getAgenda().planContinueMultiInstanceOperation(childExecution, miExecution, currentNumberOfInstances);
}
return childExecution;
}
protected ExecutionEntity searchForMultiInstanceActivity(String activityId, String parentExecutionId, ExecutionEntityManager executionEntityManager) {
List<ExecutionEntity> childExecutions = executionEntityManager.findChildExecutionsByParentExecutionId(parentExecutionId);
ExecutionEntity miExecution = null;
for (ExecutionEntity childExecution : childExecutions) {
if (activityId.equals(childExecution.getActivityId()) && childExecution.isMultiInstanceRoot()) {
if (miExecution != null) {
throw new FlowableException("Multiple multi instance executions found for activity id " + activityId + " in " + childExecution);
}
miExecution = childExecution;
}
ExecutionEntity childMiExecution = searchForMultiInstanceActivity(activityId, childExecution.getId(), executionEntityManager);
if (childMiExecution != null) {
if (miExecution != null) {
throw new FlowableException("Multiple multi instance executions found for activity id " + activityId + " in " + childExecution);
}
miExecution = childMiExecution;
}
}
return miExecution;
}
}
View on GitHub (pinned to d6d39ce1c6)
Solutions
- Inspect ACT_RU_EXECUTION for all rows with the given activity id and MULTI_INSTANCE_ROOT_OF_=1 in the process instance; delete or correct the stale duplicate (only with runtime data expertise and after backing up).
- Reproduce and fix the process definition: if nested MI activities share an activity id (or the activityId passed to addMultiInstanceExecution matches both an inner and outer MI), give each MI activity a unique id.
- If triggered by cancellation logic, ensure the MI root and its children are fully removed on termination — upgrade Flowable to a version fixing MI deletion on terminate/boundary events.
- As a workaround, delete the affected process instance and restart the process from a clean state, migrating business data manually.
- Enable debug logging for org.flowable.engine.impl.cmd to trace which executions the search visits before throwing.
Example fix
// before (ambiguous call)
runtimeService.addMultiInstanceExecution("approveTask", processInstanceId, vars);
// after (use the exact execution id of the MI root if available)
Execution miRoot = runtimeService.createExecutionQuery()
.processInstanceId(processInstanceId)
.activityId("approveTask")
.onlyChildExecutions()
.singleResult();
if (miRoot != null) {
runtimeService.addMultiInstanceExecution("approveTask", miRoot.getId(), vars);
} Defensive patterns
Strategy: validation
Validate before calling
long count = runtimeService.createExecutionQuery()
.processInstanceId(processInstanceId)
.activityId(activityId)
.count();
if (count != 1) {
throw new IllegalStateException("Expected exactly 1 execution for " + activityId + ", found " + count);
} Type guard
boolean hasSingleMiRoot(List<Execution> execs, String activityId) {
return execs.stream().filter(e -> activityId.equals(e.getActivityId())).count() == 1;
} Try / catch
try {
runtimeService.addMultiInstanceExecution(activityId, processInstanceId, vars);
} catch (FlowableException e) {
if (e.getMessage().startsWith("Multiple multi instance executions found")) {
// repair or restart the process instance
} else {
throw e;
}
} Prevention
- Give every multi-instance activity a unique id, especially in nested subprocesses.
- Before dynamic MI additions, verify exactly one MI root execution exists via ExecutionQuery.
- Ensure terminate/boundary events fully clean up MI roots; keep Flowable updated for MI lifecycle fixes.
- Avoid manual edits to ACT_RU_EXECUTION rows; back up before any repair.
When it happens
Trigger: Calling runtimeService.addMultiInstanceExecution(activityId, processInstanceId, executionVariables) when the process contains (or has wrongly persisted) two multi-instance-root executions with the same activity id — e.g. a duplicate MI root left behind by an incomplete termination/compensation, or nested MI inside MI where both parent-scope recursion and child recursion each match the same activity id.
Common situations: Historic/dirty runtime data after a failed MI loop cancellation (e.g. terminate end event or boundary event interrupted the MI root deletion); process model changes redeployed over running instances where activity ids collide across nested scopes; manual manipulation or migration of ACT_RU_EXECUTION rows.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Unable to close the local ProcessEngine
- Execution '<execution>' is not a processInstance
- Expected an activity behavior in flow node ${flowNode.getId(
- Could not find a scope execution for compensation boundary e
- Invalid number of instances: must be a non-negative integer
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/48e7249abb28bbb9.
Report an issue: GitHub.