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

  1. Add one of processDefinitionId, processDefinitionKey, or message to the request body
  2. Prefer processDefinitionKey with the latest deployed definition version if you don't have an id
  3. Use message when the process has a message start event
  4. Check JSON field naming/casing matches ProcessInstanceCreateRequest (camelCase)
  5. 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

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


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/ea6c73e1bb9830dd. Report an issue: GitHub.