{"record":{"id":"2d30345018f7d404","repo":"flowable/flowable-engine","slug":"execution-has-no-parent-execution-execution-ge","errorCode":null,"errorMessage":"Execution has no parent execution \" + execution.getParentId()","messagePattern":"Execution has no parent execution \" \\+ execution\\.getParentId\\(\\)","errorType":"exception","errorClass":"FlowableException","httpStatus":null,"severity":"error","filePath":"modules/flowable-engine/src/main/java/org/flowable/engine/impl/dynamic/AbstractDynamicStateManager.java","lineNumber":555,"sourceCode":"                                                executionEntity.setProcessDefinitionId(processInstanceChangeState.getProcessDefinitionToMigrateTo().getId());\n                                                childExecutionsToKeep.add(executionEntity.getId());\n                                            }\n                                        }\n                                    }\n                                }\n                            }\n                        }\n                    }\n                }\n\n                executionEntityManager.deleteChildExecutions(execution, childExecutionsToKeep, null, \"Change parent activity to \" + flowElementIdsLine, true, null);\n                if (!moveExecutionContainer.isDirectExecutionMigration()) {\n                    executionEntityManager.deleteExecutionAndRelatedData(execution, \"Change activity to \" + flowElementIdsLine, false, false, true, execution.getCurrentFlowElement());\n                }\n\n                // Make sure we are not moving the root execution\n                if (execution.getParentId() == null) {\n                    throw new FlowableException(\"Execution has no parent execution \" + execution.getParentId());\n                }\n\n                // Delete the parent executions for each current execution when the move to activity id has the same subProcess scope\n                ExecutionEntity continueParentExecution;\n                if (processInstanceChangeState.getProcessDefinitionToMigrateTo() != null) {\n                    continueParentExecution = deleteDirectParentExecutions(execution.getParentId(), moveToFlowElements, \n                    \t\texecutionIdsNotToDelete, processInstanceChangeState.getProcessDefinitionToMigrateTo(), moveExecutionContainer, commandContext);\n                } else {\n                    continueParentExecution = deleteParentExecutions(execution.getParentId(), moveToFlowElements, executionIdsNotToDelete, commandContext);\n                }\n                moveExecutionContainer.addContinueParentExecution(execution.getId(), continueParentExecution);\n            }\n\n            List<ExecutionEntity> newChildExecutions = createEmbeddedSubProcessAndExecutions(moveToFlowElements, executionsToMove, moveExecutionContainer, processInstanceChangeState, commandContext);\n\n            if (moveExecutionContainer.isMoveToSubProcessInstance()) {\n                CallActivity callActivity = moveExecutionContainer.getCallActivity();\n                Process subProcess = moveExecutionContainer.getSubProcessModel().getProcessById(moveExecutionContainer.getSubProcessDefKey());","sourceCodeStart":537,"sourceCodeEnd":573,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine/src/main/java/org/flowable/engine/impl/dynamic/AbstractDynamicStateManager.java#L537-L573","documentation":"During doMoveExecutionState, before moving, the code ensures it is not operating on the root execution: a null parentId means the execution is the process-instance root and cannot be deleted/re-parented as part of the move. The message is misleading — it always interpolates null since it fires only when getParentId() == null.","triggerScenarios":"A move/change-state operation resolving to the root process-instance execution as one of the executions to move (e.g. source activity id accidentally matches the process instance scope, or move container resolved the root execution).","commonSituations":"Passing the process instance's own scope id as an activity id; move containers built from executions lacking currentActivityId filtering; custom subclasses of AbstractDynamicStateManager feeding wrong executions.","solutions":["Ensure the source activity ids map to leaf/concurrent executions, not the root process instance execution","Check that filtered executions exclude the process-instance-type execution before building the move container","If implementing a custom dynamic state manager, exclude root executions from the move set"],"exampleFix":"// before\nList<ExecutionEntity> toMove = executions;\n// after\nList<ExecutionEntity> toMove = executions.stream().filter(e -> e.getParentId() != null).collect(toList());","handlingStrategy":"validation","validationCode":"if (execution.isProcessInstanceType() || execution.getParentId() == null) throw new IllegalStateException(\"Root execution cannot be moved\");","typeGuard":"boolean isMovable(ExecutionEntity e) { return e.getParentId() != null; }","tryCatchPattern":"try { builder.changeState(); } catch (FlowableException e) { if (e.getMessage().startsWith(\"Execution has no parent\")) { /* exclude root execution from move set */ } else throw e; }","preventionTips":["Filter process-instance-type executions out of move containers","Never pass scope/root ids as move activity ids","Test custom dynamic state managers against concurrent executions"],"tags":["flowable","execution","internal"],"backgroundTag":"internal-invariant-violation","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-18T11:17:12.947Z"}