flowable/flowable-engine · error · FlowableException

Historic case instance has not ended and can only be migrate

Error message

Historic case instance has not ended and can only be migrated with the regular case instance migrate method (migrateCaseInstance) for id ${caseInstanceId}

What it means

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.

Source

Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/migration/CaseInstanceMigrationManagerImpl.java:232

        CaseInstanceEntity caseInstance = caseInstanceEntityManager.findById(caseInstanceId);
        if (caseInstance == null) {
            throw new FlowableException("Cannot find the case to migrate, with id" + caseInstanceId);
        }

        CaseDefinition caseDefinitionToMigrateTo = resolveCaseDefinition(document, commandContext);
        doMigrateCaseInstance(caseInstance, caseDefinitionToMigrateTo, document, commandContext);
    }
    
    @Override
    public void migrateHistoricCaseInstance(String caseInstanceId, HistoricCaseInstanceMigrationDocument document, CommandContext commandContext) {
        HistoricCaseInstanceEntityManager historicCaseInstanceEntityManager = CommandContextUtil.getHistoricCaseInstanceEntityManager(commandContext);
        HistoricCaseInstanceEntity caseInstance = historicCaseInstanceEntityManager.findById(caseInstanceId);
        if (caseInstance == null) {
            throw new FlowableException("Cannot find the historic case instance to migrate, with id" + caseInstanceId);
        }
        
        if (!CaseInstanceState.END_STATES.contains(caseInstance.getState())) {
            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);
        }

        CaseDefinition caseDefinitionToMigrateTo = resolveCaseDefinition(document, commandContext);
        doMigrateHistoricCaseInstance(caseInstance, caseDefinitionToMigrateTo, document, commandContext);
    }

    @Override
    public void migrateCaseInstancesOfCaseDefinition(String caseDefinitionKey, int caseDefinitionVersion, String caseDefinitionTenantId, CaseInstanceMigrationDocument document, CommandContext commandContext) {
        CaseDefinition caseDefinition = resolveCaseDefinition(caseDefinitionKey, caseDefinitionVersion, caseDefinitionTenantId, commandContext);
        migrateCaseInstancesOfCaseDefinition(caseDefinition.getId(), document, commandContext);
    }
    
    @Override
    public void migrateHistoricCaseInstancesOfCaseDefinition(String caseDefinitionKey, int caseDefinitionVersion, String caseDefinitionTenantId, HistoricCaseInstanceMigrationDocument document, CommandContext commandContext) {
        CaseDefinition caseDefinition = resolveCaseDefinition(caseDefinitionKey, caseDefinitionVersion, caseDefinitionTenantId, commandContext);
        migrateHistoricCaseInstancesOfCaseDefinition(caseDefinition.getId(), document, commandContext);
    }

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Use migrationManager.migrateCaseInstance(caseId, document, commandContext) for running cases
  2. Verify the case is finished first: historyService.createHistoricCaseInstanceQuery().caseInstanceId(id).finished().count() > 0
  3. If the case should be ended, ensure the case completes before migrating

Example fix

// before
historyMigrationManager.migrateHistoricCaseInstance(runningCaseId, doc, commandContext);
// after
migrationManager.migrateCaseInstance(runningCaseId, doc, commandContext); // for active cases
Defensive patterns

Strategy: validation

Validate before calling

const hist = historyService.createHistoricCaseInstanceQuery().caseInstanceId(caseId).singleResult();
if (hist && !endStates.includes(hist.state)) throw new Error('Case ' + caseId + ' has not ended; use migrateCaseInstance');

Type guard

function hasEnded(h) { return h != null && ['completed','terminated','closed'].includes(h.state); }

Try / catch

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; }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

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/6e4825e8307a2e2d. Report an issue: GitHub.