{"record":{"id":"26217f7cbc123755","repo":"flowable/flowable-engine","slug":"tenant-mismatch-between-historic-case-instance","errorCode":null,"errorMessage":"Tenant mismatch between Historic Case Instance ('${historicCaseInstance.getTenantId()}') and Case Definition ('${destinationTenantId}') to migrate to","messagePattern":"Tenant mismatch between Historic Case Instance \\('(.+?)'\\) and Case Definition \\('(.+?)'\\) to migrate to","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":354,"sourceCode":"            }\n        }\n\n        if (document.getPostUpgradeExpression() != null && !document.getPostUpgradeExpression().isEmpty()) {\n            cmmnEngineConfiguration.getExpressionManager().createExpression(document.getPostUpgradeExpression()).getValue(caseInstance);\n        }\n    }\n\n    protected void doMigrateHistoricCaseInstance(HistoricCaseInstanceEntity historicCaseInstance, CaseDefinition caseDefinitionToMigrateTo, HistoricCaseInstanceMigrationDocument document, CommandContext commandContext) {\n        LOGGER.debug(\"Start migration of historic case instance with Id:'{}' to case definition identified by {}\", historicCaseInstance.getId(), printCaseDefinitionIdentifierMessage(document));\n        \n        String destinationTenantId = caseDefinitionToMigrateTo.getTenantId();\n        if (!Objects.equals(historicCaseInstance.getTenantId(), destinationTenantId)) {\n            \n            CmmnEngineConfiguration cmmnEngineConfiguration = CommandContextUtil.getCmmnEngineConfiguration(commandContext);\n            if (cmmnEngineConfiguration.isFallbackToDefaultTenant() && cmmnEngineConfiguration.getDefaultTenantProvider() != null) {\n                \n                if (!Objects.equals(destinationTenantId, cmmnEngineConfiguration.getDefaultTenantProvider().getDefaultTenant(historicCaseInstance.getId(), ScopeTypes.CMMN, caseDefinitionToMigrateTo.getKey()))) {\n                    throw new FlowableException(\"Tenant mismatch between Historic Case Instance ('\" + historicCaseInstance.getTenantId() + \"') and Case Definition ('\" + destinationTenantId + \"') to migrate to\");\n                }\n            \n            } else {\n                throw new FlowableException(\"Tenant mismatch between Historic Case Instance ('\" + historicCaseInstance.getTenantId() + \"') and Case Definition ('\" + destinationTenantId + \"') to migrate to\");\n            }\n        }\n        \n        LOGGER.debug(\"Updating case definition reference of case root execution with id:'{}' to '{}'\", historicCaseInstance.getId(), caseDefinitionToMigrateTo.getId());\n        String originalCaseDefinitionId = historicCaseInstance.getCaseDefinitionId();\n        historicCaseInstance.setCaseDefinitionId(caseDefinitionToMigrateTo.getId());\n        historicCaseInstance.setCaseDefinitionKey(caseDefinitionToMigrateTo.getKey());\n        historicCaseInstance.setCaseDefinitionName(caseDefinitionToMigrateTo.getName());\n        historicCaseInstance.setCaseDefinitionVersion(caseDefinitionToMigrateTo.getVersion());\n        historicCaseInstance.setCaseDefinitionDeploymentId(caseDefinitionToMigrateTo.getDeploymentId());\n        CommandContextUtil.getHistoricCaseInstanceEntityManager(commandContext).update(historicCaseInstance);\n\n        LOGGER.debug(\"Updating case definition reference in history\");\n        changeCaseDefinitionReferenceForHistoricCaseInstance(historicCaseInstance, caseDefinitionToMigrateTo, commandContext);","sourceCodeStart":336,"sourceCodeEnd":372,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/migration/CaseInstanceMigrationManagerImpl.java#L336-L372","documentation":"doMigrateHistoricCaseInstance enforces tenant consistency: if the historic case instance's tenant differs from the target case definition's tenant, migration proceeds only when fallback-to-default-tenant is enabled AND the default tenant provider returns the destination tenant; otherwise this FlowableException is thrown.","triggerScenarios":"Migrating a historic case instance to a case definition with a different tenantId, while cmmnEngineConfiguration.isFallbackToDefaultTenant() is false, or it is true but getDefaultTenantProvider().getDefaultTenant(...) does not equal the destination tenantId.","commonSituations":"Multi-tenant deployments where the historic data lives under tenant 'a' but the new definition was deployed to tenant 'b'; enabling fallback without configuring a DefaultTenantProvider that maps the instance to the destination tenant; moving cases across tenants during a reorganization.","solutions":["Migrate to a case definition deployed under the same tenant as the historic instances","Enable fallback: cmmnEngineConfiguration.setFallbackDefaultTenant(true) and set a DefaultTenantProvider that returns destinationTenantId for the instance/scope","Change the destination definition's tenant to match the historic instances' tenant","Re-tag historic instances' tenant data if a cross-tenant move is genuinely required"],"exampleFix":"// before\ncmmnEngineConfiguration.setFallbackDefaultTenant(false); // mismatch throws\n// after\ncmmnEngineConfiguration.setFallbackDefaultTenant(true);\ncmmnEngineConfiguration.setDefaultTenantProvider((caseInstanceId, scopeType, caseDefinitionKey) -> destinationTenantId);","handlingStrategy":"validation","validationCode":"const hist = historyService.createHistoricCaseInstanceQuery().caseInstanceId(id).singleResult();\nif (hist.tenantId !== targetDefinition.tenantId && !engineConfig.fallbackDefaultTenant) {\n  throw new Error(`Tenant mismatch: instance '${hist.tenantId}' vs definition '${targetDefinition.tenantId}'`);\n}","typeGuard":"function tenantsCompatible(h, def, fallback, provider) { return h.tenantId === def.tenantId || (fallback && provider && provider.getDefaultTenant(h.id, 'cmmn', def.key) === def.tenantId); }","tryCatchPattern":"try { migrateHistoricCaseInstance(id, doc); } catch (e) { if (String(e.message).startsWith('Tenant mismatch between Historic Case Instance')) { alignTenantOrEnableFallback(e); } else { throw e; } }","preventionTips":["Keep target case definitions deployed under the same tenant as the historic data","If cross-tenant migration is needed, configure fallbackDefaultTenant with a matching DefaultTenantProvider before migrating","Audit tenantId values in both definitions and historic rows as part of migration dry-run"],"tags":["cmmn","migration","tenant","multi-tenancy"],"backgroundTag":"invalid-argument-value","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"}