flowable/flowable-engine · error · FlowableIllegalArgumentException

Only one of caseDefinitionId or caseDefinitionKey should be…

Error message

Only one of caseDefinitionId or caseDefinitionKey should be set.

What it means

createCaseInstance allows only one way of resolving the case definition. If the request sets both caseDefinitionId and caseDefinitionKey (more than one parameter set), Flowable throws FlowableIllegalArgumentException (HTTP 400) because the target definition would be ambiguous.

Solutions

  1. Remove caseDefinitionId and keep only caseDefinitionKey (preferred when targeting the latest version)
  2. Or keep only caseDefinitionId when you must pin an exact deployed definition version
  3. Fix the client mapping so it sets exactly one of the two fields
  4. Validate the payload client-side before sending

Example fix

// before
{"caseDefinitionId":"myCase:1:4", "caseDefinitionKey":"myCase"}
// after
{"caseDefinitionKey":"myCase"}
Defensive patterns

Strategy: validation

Validate before calling

int set = (request.getCaseDefinitionId() != null ? 1 : 0) + (request.getCaseDefinitionKey() != null ? 1 : 0);
if (set > 1) throw new IllegalArgumentException("Set only one of caseDefinitionId / caseDefinitionKey");

Type guard

null

Try / catch

try {
    startCase(request);
} catch (HttpClientErrorException.BadRequest e) {
    if (e.getResponseBodyAsString().contains("Only one of caseDefinitionId or caseDefinitionKey")) {
        request.setCaseDefinitionId(null); // keep key only and retry
    }
}

Prevention

When it happens

Trigger: POST /cmmn-runtime/case-instances with a body containing both caseDefinitionId and caseDefinitionKey (paramsSet > 1).

Common situations: Client code that always populates both fields from separate config values, template payloads copied between examples, generic start-form code that forwards all known definition fields.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/1988db9c9bf728d6. 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:338

            + "More information about the variable format can be found in the REST variables section.\n\n "
            + "Note that the variable-scope that is supplied is ignored, case-variables are always local.\n\n",
            code = 201)
    @ApiResponses(value = {
            @ApiResponse(code = 201, message = "Indicates the case instance was created."),
            @ApiResponse(code = 400, message = "Indicates either the case definition was not found (based on id or key), no process is started by sending the given message or an invalid variable has been passed. Status description contains additional information about the error.")
    })
    @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.");
                }

View on GitHub (pinned to d6d39ce1c6)