flowable/flowable-engine · error · FlowableIllegalArgumentException
Illegal action: ' '.
Error message
Illegal action: '${action}'. What it means
FlowableIllegalArgumentException thrown by ExecutionCollectionResource.executeExecutionAction when the ExecutionActionRequest 'action' is anything other than 'signal-event-received' — the only action this collection-level endpoint supports. This endpoint signals the whole process instance via a signal event; other execution actions must go through the single-execution endpoint.
Solutions
- Set action to exactly "signal-event-received" in the request body.
- Use the per-execution endpoint POST /runtime/executions/{executionId} instead if you need 'trigger' or 'message-event-received' actions.
- Check for typos/casing: the value is kebab-case and case-sensitive.
- Consult the ExecutionActionRequest.ACTION_SIGNAL_EVENT_RECEIVED constant for the canonical value.
Example fix
// before
{"action": "message-event-received", "messageName": "orderMsg"}
// after
{"action": "signal-event-received", "signalName": "orderSignal"} Defensive patterns
Strategy: validation
Validate before calling
const ALLOWED_ACTIONS = ['signal-event-received']; // collection endpoint supports only this
if (!ALLOWED_ACTIONS.includes(body.action)) {
throw new Error(`POST /runtime/executions only supports action "signal-event-received", got "${body.action}"`);
} Type guard
function isSignalAction(a: unknown): a is 'signal-event-received' {
return a === 'signal-event-received';
} Try / catch
try {
await restClient.post('/runtime/executions', body);
} catch (e) {
if (e.response && e.response.status === 400 && /Illegal action/.test(e.response.data && e.response.data.message || '')) {
console.error(`Unsupported action "${body.action}" for this endpoint; use the per-execution endpoint for trigger/message actions`);
}
throw e;
} Prevention
- Use string constants (ExecutionActionRequest.ACTION_SIGNAL_EVENT_RECEIVED) instead of literals.
- Remember kebab-case values: 'signal-event-received', 'message-event-received', 'trigger'.
- Route trigger/message actions to POST /runtime/executions/{executionId}, not the collection endpoint.
- Keep an API-client wrapper that exposes typed methods (signalProcessInstance) instead of raw action strings.
When it happens
Trigger: POST /runtime/executions with body {"action":"..."} where action is not "signal-event-received" (e.g. "message-event-received", "trigger", or a typo like "signalEventReceived").
Common situations: Copying action strings from code that targets the per-execution endpoint (which allows trigger/message actions); camelCase vs kebab-case confusion with ExecutionActionRequest.ACTION_* constants; using old Activiti-style action names.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- A request body was expected when executing the form submit.
- Attachment name is required.
- Error converting request body to RestVariable instance
- Id cannot be null.
- Invalid action: ' '.
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/820481daf0432673.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/process/ExecutionCollectionResource.java:145
return getQueryResponse(queryRequest, allRequestParams);
}
//FIXME Documentation ?
@ApiOperation(value = "Signal event received", tags = { "Executions" }, code = 204)
@ApiResponses(value = {
@ApiResponse(code = 204, message = "Indicates request was successful and the executions are returned"),
@ApiResponse(code = 404, message = "Indicates a parameter was passed in the wrong format . The status-message contains additional information.")
})
@PutMapping(value = "/runtime/executions")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void executeExecutionAction(@RequestBody ExecutionActionRequest actionRequest) {
if (restApiInterceptor != null) {
restApiInterceptor.doExecutionActionRequest(actionRequest);
}
if (!ExecutionActionRequest.ACTION_SIGNAL_EVENT_RECEIVED.equals(actionRequest.getAction())) {
throw new FlowableIllegalArgumentException("Illegal action: '" + actionRequest.getAction() + "'.");
}
if (actionRequest.getSignalName() == null) {
throw new FlowableIllegalArgumentException("Signal name is required.");
}
if (actionRequest.getVariables() != null) {
runtimeService.signalEventReceived(actionRequest.getSignalName(), getVariablesToSet(actionRequest.getVariables()));
} else {
runtimeService.signalEventReceived(actionRequest.getSignalName());
}
}
}
View on GitHub (pinned to d6d39ce1c6)