flowable/flowable-engine · error · FlowableIllegalArgumentException
Either processDefinitionId, processDefinitionKey or message
Error message
Either processDefinitionId, processDefinitionKey or message is required.
What it means
FlowableIllegalArgumentException thrown by createProcessInstance (POST /runtime/process-instances) when none of processDefinitionId, processDefinitionKey, or message is set on the request body. Flowable needs at least one of these three ways to locate what to start; an empty start request is rejected before the engine is invoked. Exactly one of the three must be provided.
Source
Thrown at modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/process/ProcessInstanceCollectionResource.java:331
@ApiOperation(value = "Start a process instance", tags = { "Process Instances" },
notes = "Note that also a *transientVariables* property is accepted as part of this json, that follows the same structure as the *variables* property.\n\n"
+ "Only one of *processDefinitionId*, *processDefinitionKey* or *message* can be used in the request body. \n\n"
+ "Parameters *businessKey*, *variables* and *tenantId* are optional.\n\n"
+ "If tenantId is omitted, the default tenant will be used.\n\n "
+ "It is possible to send variables, transientVariables and startFormVariables in one request.\n\n"
+ "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, process-variables are always local.\n\n",
code = 201)
@ApiResponses(value = {
@ApiResponse(code = 201, message = "Indicates the process instance was created."),
@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.")
})
@PostMapping(value = "/runtime/process-instances", produces = "application/json")
@ResponseStatus(HttpStatus.CREATED)
public ProcessInstanceResponse createProcessInstance(@RequestBody ProcessInstanceCreateRequest request) {
if (request.getProcessDefinitionId() == null && request.getProcessDefinitionKey() == null && request.getMessage() == null) {
throw new FlowableIllegalArgumentException("Either processDefinitionId, processDefinitionKey or message is required.");
}
int paramsSet = ((request.getProcessDefinitionId() != null) ? 1 : 0) + ((request.getProcessDefinitionKey() != null) ? 1 : 0) + ((request.getMessage() != null) ? 1 : 0);
if (paramsSet > 1) {
throw new FlowableIllegalArgumentException("Only one of processDefinitionId, processDefinitionKey or message should be set.");
}
if (request.isTenantSet()) {
// Tenant-id can only be used with either key or message
if (request.getProcessDefinitionId() != null) {
throw new FlowableIllegalArgumentException("TenantId can only be used with either processDefinitionKey or message.");
}
}
Map<String, Object> startVariables = null;
Map<String, Object> transientVariables = null;
Map<String, Object> startFormVariables = null;View on GitHub (pinned to d6d39ce1c6)
Solutions
- Add one of processDefinitionId, processDefinitionKey, or message to the request body
- Prefer processDefinitionKey with the latest deployed definition version if you don't have an id
- Use message when the process has a message start event
- Check JSON field naming/casing matches ProcessInstanceCreateRequest (camelCase)
- Confirm the client serializer is not nulling out fields you set
Example fix
// before
POST /runtime/process-instances
{"variables":{"a":1}}
// after
POST /runtime/process-instances
{"processDefinitionKey":"orderProcess","variables":{"a":1}} Defensive patterns
Strategy: validation
Validate before calling
const starters = ['processDefinitionId','processDefinitionKey','message'].filter(k => req[k] != null && req[k] !== '');
if (starters.length === 0) throw new Error('Provide exactly one of processDefinitionId, processDefinitionKey, or message'); Type guard
function hasStartRef(req) {
return ['processDefinitionId','processDefinitionKey','message'].some(k => typeof req?.[k] === 'string' && req[k].length > 0);
} Try / catch
try { await startProcessInstance(req); } catch (e) { if (e.status === 400 && /Either processDefinitionId/.test(e.message)) { console.error('Add a start reference to the request'); } throw e; } Prevention
- Always set processDefinitionKey as the default in start-request builders
- Validate the request object before serialization
- Check field name casing survives serialization (camelCase)
When it happens
Trigger: POST /runtime/process-instances with a JSON body that omits all three fields or has them all as null/empty strings.
Common situations: Building the request programmatically with a variable map but forgetting the definition reference, deserialization dropping unknown/mismatched field names (e.g. process_definition_id in the payload), test fixtures with empty request objects.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- Variable name is required.
- Comment text is required.
- Variable operation is missing for variable: ${variable.getNa
- Variable value is missing for variable: ${variable.getName()
- Value-only query (without a variable-name) is not supported.
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/ea6c73e1bb9830dd.
Report an issue: GitHub.