{"record":{"id":"460696e4e4e193c9","repo":"flowable/flowable-engine","slug":"only-one-of-processdefinitionid-processdefinition","errorCode":null,"errorMessage":"Only one of processDefinitionId, processDefinitionKey or message should be set.","messagePattern":"Only one of processDefinitionId, processDefinitionKey or message should be set\\.","errorType":"validation","errorClass":"FlowableIllegalArgumentException","httpStatus":400,"severity":"error","filePath":"modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/process/ProcessInstanceCollectionResource.java","lineNumber":337,"sourceCode":"            + \"More information about the variable format can be found in the REST variables section.\\n\\n \"\n            + \"Note that the variable-scope that is supplied is ignored, process-variables are always local.\\n\\n\",\n            code = 201)\n    @ApiResponses(value = {\n            @ApiResponse(code = 201, message = \"Indicates the process instance was created.\"),\n            @ApiResponse(code = 400, message = \"Indicates either the process-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.\")\n    })\n    @PostMapping(value = \"/runtime/process-instances\", produces = \"application/json\")\n    @ResponseStatus(HttpStatus.CREATED)\n    public ProcessInstanceResponse createProcessInstance(@RequestBody ProcessInstanceCreateRequest request) {\n\n        if (request.getProcessDefinitionId() == null && request.getProcessDefinitionKey() == null && request.getMessage() == null) {\n            throw new FlowableIllegalArgumentException(\"Either processDefinitionId, processDefinitionKey or message is required.\");\n        }\n\n        int paramsSet = ((request.getProcessDefinitionId() != null) ? 1 : 0) + ((request.getProcessDefinitionKey() != null) ? 1 : 0) + ((request.getMessage() != null) ? 1 : 0);\n\n        if (paramsSet > 1) {\n            throw new FlowableIllegalArgumentException(\"Only one of processDefinitionId, processDefinitionKey or message should be set.\");\n        }\n\n        if (request.isTenantSet()) {\n            // Tenant-id can only be used with either key or message\n            if (request.getProcessDefinitionId() != null) {\n                throw new FlowableIllegalArgumentException(\"TenantId can only be used with either processDefinitionKey or message.\");\n            }\n        }\n        \n        Map<String, Object> startVariables = null;\n        Map<String, Object> transientVariables = null;\n        Map<String, Object> startFormVariables = null;\n        if (request.getStartFormVariables() != null && !request.getStartFormVariables().isEmpty()) {\n            startFormVariables = new HashMap<>();\n            for (RestVariable variable : request.getStartFormVariables()) {\n                if (variable.getName() == null) {\n                    throw new FlowableIllegalArgumentException(\"Variable name is required.\");\n                }","sourceCodeStart":319,"sourceCodeEnd":355,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/process/ProcessInstanceCollectionResource.java#L319-L355","documentation":"FlowableIllegalArgumentException thrown by createProcessInstance when more than one of processDefinitionId, processDefinitionKey, or message is set in the request. The three fields are mutually exclusive: each names a different start mechanism (by id, by key, or by message start event), so Flowable refuses to guess. Exactly one must be supplied.","triggerScenarios":"POST /runtime/process-instances with two or all three of processDefinitionId/processDefinitionKey/message non-null in the body, e.g. copying a full config object into the request.","commonSituations":"Serializing an object that carries both key and message from application state, clients adding message for signal/event support while a key is already present, templates pre-filled with defaults not cleared.","solutions":["Remove all but one of processDefinitionId, processDefinitionKey, and message from the request","Prefer processDefinitionId when you have it; otherwise keep processDefinitionKey and drop message","Null out unused fields explicitly before sending","Sanitize/whitelist fields when mapping internal objects into the REST request"],"exampleFix":"// before\n{\"processDefinitionKey\":\"orderProcess\",\"message\":\"orderReceived\"}\n// after\n{\"processDefinitionKey\":\"orderProcess\"}","handlingStrategy":"validation","validationCode":"const set = ['processDefinitionId','processDefinitionKey','message'].filter(k => req[k] != null);\nif (set.length > 1) throw new Error(`Only one start reference allowed, got: ${set.join(',')}`);","typeGuard":"function hasExactlyOneStartRef(req) {\n  const n = ['processDefinitionId','processDefinitionKey','message'].filter(k => req?.[k] != null).length;\n  return n === 1;\n}","tryCatchPattern":"try { await startProcessInstance(req); } catch (e) { if (e.status === 400 && /Only one of/.test(e.message)) { stripExtraStartRefs(req); } throw e; }","preventionTips":["Explicitly null unused start-reference fields before sending","Whitelist fields when mapping internal objects into the REST request","Prefer processDefinitionId over key/message when an id is known"],"tags":["rest","validation","conflicting-options","flowable"],"backgroundTag":"mutually-exclusive-options","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-18T11:17:12.947Z"}