{"record":{"id":"6e4825e8307a2e2d","repo":"flowable/flowable-engine","slug":"historic-case-instance-has-not-ended-and-can-only","errorCode":null,"errorMessage":"Historic case instance has not ended and can only be migrated with the regular case instance migrate method (migrateCaseInstance) for id ${caseInstanceId}","messagePattern":"Historic case instance has not ended and can only be migrated with the regular case instance migrate method \\(migrateCaseInstance\\) for id (.+?)","errorType":"exception","errorClass":"FlowableException","httpStatus":null,"severity":"error","filePath":"modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/migration/CaseInstanceMigrationManagerImpl.java","lineNumber":232,"sourceCode":"        CaseInstanceEntity caseInstance = caseInstanceEntityManager.findById(caseInstanceId);\n        if (caseInstance == null) {\n            throw new FlowableException(\"Cannot find the case to migrate, with id\" + caseInstanceId);\n        }\n\n        CaseDefinition caseDefinitionToMigrateTo = resolveCaseDefinition(document, commandContext);\n        doMigrateCaseInstance(caseInstance, caseDefinitionToMigrateTo, document, commandContext);\n    }\n    \n    @Override\n    public void migrateHistoricCaseInstance(String caseInstanceId, HistoricCaseInstanceMigrationDocument document, CommandContext commandContext) {\n        HistoricCaseInstanceEntityManager historicCaseInstanceEntityManager = CommandContextUtil.getHistoricCaseInstanceEntityManager(commandContext);\n        HistoricCaseInstanceEntity caseInstance = historicCaseInstanceEntityManager.findById(caseInstanceId);\n        if (caseInstance == null) {\n            throw new FlowableException(\"Cannot find the historic case instance to migrate, with id\" + caseInstanceId);\n        }\n        \n        if (!CaseInstanceState.END_STATES.contains(caseInstance.getState())) {\n            throw new FlowableException(\"Historic case instance has not ended and can only be migrated with the regular case instance migrate method (migrateCaseInstance) for id \" + caseInstanceId);\n        }\n\n        CaseDefinition caseDefinitionToMigrateTo = resolveCaseDefinition(document, commandContext);\n        doMigrateHistoricCaseInstance(caseInstance, caseDefinitionToMigrateTo, document, commandContext);\n    }\n\n    @Override\n    public void migrateCaseInstancesOfCaseDefinition(String caseDefinitionKey, int caseDefinitionVersion, String caseDefinitionTenantId, CaseInstanceMigrationDocument document, CommandContext commandContext) {\n        CaseDefinition caseDefinition = resolveCaseDefinition(caseDefinitionKey, caseDefinitionVersion, caseDefinitionTenantId, commandContext);\n        migrateCaseInstancesOfCaseDefinition(caseDefinition.getId(), document, commandContext);\n    }\n    \n    @Override\n    public void migrateHistoricCaseInstancesOfCaseDefinition(String caseDefinitionKey, int caseDefinitionVersion, String caseDefinitionTenantId, HistoricCaseInstanceMigrationDocument document, CommandContext commandContext) {\n        CaseDefinition caseDefinition = resolveCaseDefinition(caseDefinitionKey, caseDefinitionVersion, caseDefinitionTenantId, commandContext);\n        migrateHistoricCaseInstancesOfCaseDefinition(caseDefinition.getId(), document, commandContext);\n    }\n","sourceCodeStart":214,"sourceCodeEnd":250,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/migration/CaseInstanceMigrationManagerImpl.java#L214-L250","documentation":"migrateHistoricCaseInstance only accepts historic (ended) case instances. If the found historic case instance's state is not in CaseInstanceState.END_STATES, this FlowableException tells you to use the regular migrateCaseInstance method instead.","triggerScenarios":"Calling migrateHistoricCaseInstance with the id of a case instance that is still running/active — its historic record exists (or its state is non-terminal) but it has not reached an end state.","commonSituations":"Migrating active cases with the historic API by mistake; a case that appears in history queries but is still in progress; copy-paste between the two migration code paths.","solutions":["Use migrationManager.migrateCaseInstance(caseId, document, commandContext) for running cases","Verify the case is finished first: historyService.createHistoricCaseInstanceQuery().caseInstanceId(id).finished().count() > 0","If the case should be ended, ensure the case completes before migrating"],"exampleFix":"// before\nhistoryMigrationManager.migrateHistoricCaseInstance(runningCaseId, doc, commandContext);\n// after\nmigrationManager.migrateCaseInstance(runningCaseId, doc, commandContext); // for active cases","handlingStrategy":"validation","validationCode":"const hist = historyService.createHistoricCaseInstanceQuery().caseInstanceId(caseId).singleResult();\nif (hist && !endStates.includes(hist.state)) throw new Error('Case ' + caseId + ' has not ended; use migrateCaseInstance');","typeGuard":"function hasEnded(h) { return h != null && ['completed','terminated','closed'].includes(h.state); }","tryCatchPattern":"try { migrateHistoricCaseInstance(id, doc); } catch (e) { if (String(e.message).includes('has not ended and can only be migrated')) { return migrateCaseInstance(id, doc); } throw e; }","preventionTips":["Decide live vs historic migration based on case state, not call-site convenience","Add a helper that dispatches to the right migration method automatically","Monitor in-flight cases before starting historic migration batches"],"tags":["cmmn","migration","invalid-state","history"],"backgroundTag":"invalid-state-transition","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}