flowable/flowable-engine · error · FlowableIllegalArgumentException
TenantId can only be used with either caseDefinitionKey.
Error message
TenantId can only be used with either caseDefinitionKey.
What it means
A tenantId in a case instance start request can only be combined with caseDefinitionKey (so the engine resolves the latest definition of that key within the tenant). If tenantId is set together with caseDefinitionId, createCaseInstance throws FlowableIllegalArgumentException (HTTP 400) because a definition id already pins the definition and tenant ambiguity is not resolvable.
Solutions
- Drop tenantId from the request and use only caseDefinitionId
- Or drop caseDefinitionId and use caseDefinitionKey plus tenantId so the tenant-scoped latest version is started
- Adjust client/interceptor code that automatically injects tenantId into all start requests
Example fix
// before
{"caseDefinitionId":"myCase:1:4", "tenantId":"tenantA"}
// after
{"caseDefinitionKey":"myCase", "tenantId":"tenantA"} Defensive patterns
Strategy: validation
Validate before calling
if (request.isTenantSet() && request.getCaseDefinitionId() != null) {
throw new IllegalArgumentException("tenantId cannot be combined with caseDefinitionId; use caseDefinitionKey");
} Type guard
null
Try / catch
try {
startCase(request);
} catch (HttpClientErrorException.BadRequest e) {
if (e.getResponseBodyAsString().contains("TenantId can only be used with either caseDefinitionKey")) {
request.setCaseDefinitionId(null);
request.setCaseDefinitionKey(resolveKeyFromId(request));
}
} Prevention
- Use caseDefinitionKey + tenantId for multi-tenant starts
- Do not blanket-inject tenantId into all start requests
- Document that tenantId is only valid with key-based resolution
When it happens
Trigger: POST /cmmn-runtime/case-instances with request.isTenantSet() == true and caseDefinitionId != null.
Common situations: Multi-tenant clients that blanket-add tenantId to every start request, copy-pasted payloads combining all optional fields, framework code that injects tenant context into requests that use definition ids.
Understand the failure class
Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.
Related errors
- caseInstance tenant id is null
- Either caseDefinitionId or caseDefinitionKey is required.
- Must specify a case definition tenant id to migrate
- Only 'binary' and 'serializable' are supported as variable…
- Only one of caseDefinitionId or caseDefinitionKey should be…
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/65fbc4d74818cfd1.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/rest/service/api/runtime/caze/CaseInstanceCollectionResource.java:344
})
@PostMapping(value = "/cmmn-runtime/case-instances", produces = "application/json")
@ResponseStatus(HttpStatus.CREATED)
public CaseInstanceResponse createCaseInstance(@RequestBody CaseInstanceCreateRequest request) {
if (request.getCaseDefinitionId() == null && request.getCaseDefinitionKey() == null) {
throw new FlowableIllegalArgumentException("Either caseDefinitionId or caseDefinitionKey is required.");
}
int paramsSet = ((request.getCaseDefinitionId() != null) ? 1 : 0) + ((request.getCaseDefinitionKey() != null) ? 1 : 0);
if (paramsSet > 1) {
throw new FlowableIllegalArgumentException("Only one of caseDefinitionId or caseDefinitionKey should be set.");
}
if (request.isTenantSet()) {
// Tenant-id can only be used with either key or message
if (request.getCaseDefinitionId() != null) {
throw new FlowableIllegalArgumentException("TenantId can only be used with either caseDefinitionKey.");
}
}
Map<String, Object> startVariables = null;
Map<String, Object> transientVariables = null;
Map<String, Object> startFormVariables = null;
if (request.getStartFormVariables() != null) {
startFormVariables = new HashMap<>();
for (RestVariable variable : request.getStartFormVariables()) {
if (variable.getName() == null) {
throw new FlowableIllegalArgumentException("Variable name is required.");
}
startFormVariables.put(variable.getName(), restResponseFactory.getVariableValue(variable));
}
}
if (request.getVariables() != null) {View on GitHub (pinned to d6d39ce1c6)