flowable/flowable-engine · error · FlowableException
Tenant mismatch between Case Instance ('${caseInstance.getTe
Error message
Tenant mismatch between Case Instance ('${caseInstance.getTenantId()}') and Case Definition ('${destinationTenantId}') to migrate to What it means
When changing plan item state (live case instance migration), Flowable validates the case instance's tenant against the destination case definition's tenant. On mismatch, it only allows the operation if fallback-to-default-tenant is enabled and the default tenant provider yields the destination tenant; otherwise this FlowableException is thrown.
Source
Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/migration/CaseInstanceMigrationManagerImpl.java:393
if (migrationCallbacks != null && !migrationCallbacks.isEmpty()) {
CaseDefinition sourceCaseDefinition = CommandContextUtil.getCaseDefinitionEntityManager(commandContext).findById(originalCaseDefinitionId);
for (CaseInstanceMigrationCallback caseInstanceMigrationCallback : migrationCallbacks) {
caseInstanceMigrationCallback.historicCaseInstanceMigrated(historicCaseInstance, sourceCaseDefinition, caseDefinitionToMigrateTo, document);
}
}
}
protected ChangePlanItemStateBuilderImpl prepareChangeStateBuilder(CaseInstance caseInstance, CaseDefinition caseDefinitionToMigrateTo,
CaseInstanceMigrationDocument document, CommandContext commandContext) {
String destinationTenantId = caseDefinitionToMigrateTo.getTenantId();
if (!Objects.equals(caseInstance.getTenantId(), destinationTenantId)) {
CmmnEngineConfiguration cmmnEngineConfiguration = CommandContextUtil.getCmmnEngineConfiguration(commandContext);
if (cmmnEngineConfiguration.isFallbackToDefaultTenant() && cmmnEngineConfiguration.getDefaultTenantProvider() != null) {
if (!Objects.equals(destinationTenantId, cmmnEngineConfiguration.getDefaultTenantProvider().getDefaultTenant(caseInstance.getTenantId(), ScopeTypes.CMMN, caseDefinitionToMigrateTo.getKey()))) {
throw new FlowableException("Tenant mismatch between Case Instance ('" + caseInstance.getTenantId() + "') and Case Definition ('" + destinationTenantId + "') to migrate to");
}
} else {
throw new FlowableException("Tenant mismatch between Case Instance ('" + caseInstance.getTenantId() + "') and Case Definition ('" + destinationTenantId + "') to migrate to");
}
}
String caseInstanceId = caseInstance.getId();
ChangePlanItemStateBuilderImpl changePlanItemStateBuilder = new ChangePlanItemStateBuilderImpl();
changePlanItemStateBuilder.caseInstanceId(caseInstanceId);
List<String> mappedPlanItemDefinitionIds = new ArrayList<>();
for (ActivatePlanItemDefinitionMapping planItemDefinitionMapping : document.getActivatePlanItemDefinitionMappings()) {
mappedPlanItemDefinitionIds.add(planItemDefinitionMapping.getPlanItemDefinitionId());
changePlanItemStateBuilder.activatePlanItemDefinition(planItemDefinitionMapping);
}
for (TerminatePlanItemDefinitionMapping planItemDefinitionMapping : document.getTerminatePlanItemDefinitionMappings()) {View on GitHub (pinned to d6d39ce1c6)
Solutions
- Pass a destination case definition with the same tenant id as the running case instance.
- Enable fallbackToDefaultTenant on CmmnEngineConfiguration and provide a DefaultTenantProvider resolving to the destination tenant.
- Redeploy the target case definition under the case instance's tenant.
- Verify the case instance tenant via the runtime service and align the change-state request.
Example fix
// before
caseService.createChangePlanItemStateBuilder()
.movePlanItemIdTo("pi1", "newStage"); // target def tenant 'b', instance tenant 'a'
// after
cmmnEngineConfiguration.setFallbackToDefaultTenant(true);
cmmnEngineConfiguration.setDefaultTenantProvider(...); // resolve 'a' -> 'b' or align tenants Defensive patterns
Strategy: validation
Validate before calling
if (!Objects.equals(caseInstance.getTenantId(), destinationDefinition.getTenantId())) {
// align or ensure fallback config before invoking changePlanItemState
} Try / catch
try { builder.changeState(); } catch (FlowableException e) { if (e.getMessage().contains("Tenant mismatch")) { /* align tenants or configure provider */ } throw e; } Prevention
- Match destination definition tenant to the running case instance's tenant.
- Configure and test DefaultTenantProvider when using fallback.
- Avoid hardcoding tenant ids in change-state scripts.
- Log both tenant ids before performing state changes.
When it happens
Trigger: Calling CaseService/ChangePlanItemStateBuilder with a case definition whose tenantId differs from caseInstance.getTenantId(), while cmmnEngineConfiguration.isFallbackToDefaultTenant() is false or no DefaultTenantProvider is configured.
Common situations: Multi-tenant deployments where a case definition was re-deployed under another tenant; programmatic plan item state changes after tenant re-organization; migration tooling that omits the tenant id.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Must specify a case definition tenant id to migrate
- Tenant mismatch between Historic Case Instance ('${historicC
- Can only trigger a human task plan item that is in the ACTIV
- No case definition found for key '${caseDefinitionKey}'. Fal
- Case definition with key '${caseDefinitionKey}' and tenantId
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/0764a694fc749e9e.
Report an issue: GitHub.