flowable/flowable-engine · error · FlowableIllegalArgumentException
Invalid action, only 'move' or 'moveToHistoryJob' is…
Error message
Invalid action, only 'move' or 'moveToHistoryJob' is supported.
What it means
Guard in JobResource.executeDeadLetterJobAction: the action field in the request body for moving a dead letter job is something other than the two supported values 'move' and 'moveToHistoryJob', so the endpoint refuses the request.
Solutions
- Use action 'move' to retry the job as executable, or 'moveToHistoryJob' to archive it
- Match the action strings exactly, camelCase with no separators
- Verify the request body is sent as JSON
Example fix
// before
{"action":"reschedule"}
// after
{"action":"moveToHistoryJob"} Defensive patterns
Strategy: validation
Validate before calling
const allowed = ['move','moveToHistoryJob'];
if (!body || !allowed.includes(body.action)) throw new Error("deadletter action must be 'move' or 'moveToHistoryJob'"); Type guard
function isValidDeadLetterAction(a) { return a === 'move' || a === 'moveToHistoryJob'; } Try / catch
try { await post(`/cmmn-management/deadletter-jobs/${id}`, body); }
catch (e) { if (e.status === 400 && /Invalid action/.test(e.message)) { /* fix action to move or moveToHistoryJob */ } else throw e; } Prevention
- Map each job endpoint to its exact set of valid actions
- Avoid stringly-typed actions in client code — use enums
- Re-read Swagger docs when porting scripts between endpoints
When it happens
Trigger: POST /cmmn-management/deadletter-jobs/{jobId} with null body or an unsupported action such as 'execute' or 'reschedule'.
Common situations: Scripts reused from the timer-jobs endpoint (move/reschedule) applied to deadletter jobs, or typos like 'move-to-history-job'.
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
- Either channelDefinitionId or channelDefinitionKey is…
- Either eventDefinitionId or eventDefinitionKey is required.
- Invalid action, only 'move' or 'reschedule' are supported.
- Variable operation is missing for variable:
- Variable value is missing for variable:
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/5de24c8d2a8f4055.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/rest/service/api/management/JobResource.java:301
try {
managementService.executeHistoryJob(historyJob.getId());
} catch (FlowableObjectNotFoundException e) {
// Re-throw to have consistent error-messaging across REST-api
throw new FlowableObjectNotFoundException("Could not find a job with id '" + jobId + "'.", Job.class);
}
}
@ApiOperation(value = "Move a single deadletter job. Accepts 'move' and 'moveToHistoryJob' as action.", tags = { "Jobs" }, code = 204)
@ApiResponses(value = {
@ApiResponse(code = 204, message = "Indicates the dead letter job was moved. Response-body is intentionally empty."),
@ApiResponse(code = 404, message = "Indicates the requested job was not found."),
@ApiResponse(code = 500, message = "Indicates the an exception occurred while executing the job. The status-description contains additional detail about the error. The full error-stacktrace can be fetched later on if needed.")
})
@PostMapping("/cmmn-management/deadletter-jobs/{jobId}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void executeDeadLetterJobAction(@ApiParam(name = "jobId") @PathVariable String jobId, @RequestBody RestActionRequest actionRequest) {
if (actionRequest == null || !(MOVE_ACTION.equals(actionRequest.getAction()) || MOVE_TO_HISTORY_JOB_ACTION.equals(actionRequest.getAction()))) {
throw new FlowableIllegalArgumentException("Invalid action, only 'move' or 'moveToHistoryJob' is supported.");
}
Job deadLetterJob = getDeadLetterJobById(jobId);
if (MOVE_ACTION.equals(actionRequest.getAction())) {
if (restApiInterceptor != null) {
restApiInterceptor.moveDeadLetterJob(deadLetterJob, MOVE_ACTION);
}
/*
* Note that the jobType is checked to know which kind of move that needs to be done.
* The MOVE_TO_HISTORY_JOB_ACTION allows to specifically force the move to a history job and trigger the else part below.
*/
try {
if (HistoryJobEntity.HISTORY_JOB_TYPE.equals(deadLetterJob.getJobType())) {
managementService.moveDeadLetterJobToHistoryJob(deadLetterJob.getId(), cmmnEngineConfiguration.getAsyncExecutorNumberOfRetries());
View on GitHub (pinned to d6d39ce1c6)