flowable/flowable-engine · warning · FlowableIllegalArgumentException
Invalid action, only 'move' or 'moveToHistoryJob' is…
Error message
Invalid action, only 'move' or 'moveToHistoryJob' is supported.
What it means
executeDeadLetterJobAction accepts a bulk action request and only supports the actions 'move' (back to regular jobs) or 'moveToHistoryJob'. Any other (or missing) action string throws FlowableIllegalArgumentException with a 400 response.
Solutions
- Set action to exactly 'move' or 'moveToHistoryJob' (case-sensitive) in the request body.
- Ensure the JSON body is present and Content-Type is application/json.
- Update legacy clients that used an older action vocabulary.
- If you want plain retry semantics, use 'move' which moves dead-letter jobs back to the executable job table.
Example fix
// before
{"action": "retry", "jobIds": ["42"]}
// after
{"action": "move", "jobIds": ["42"]} Defensive patterns
Strategy: validation
Validate before calling
const ALLOWED_ACTIONS = ['move', 'moveToHistoryJob'];
if (!ALLOWED_ACTIONS.includes(actionRequest?.action)) {
throw new Error(`action must be one of ${ALLOWED_ACTIONS.join(', ')}`);
} Type guard
function isValidDeadLetterAction(r) {
return !!r && (r.action === 'move' || r.action === 'moveToHistoryJob');
} Try / catch
try {
await bulkMoveDeadLetterJobs(req);
} catch (e) {
if (e.status === 400) { /* fix action string to 'move' or 'moveToHistoryJob' */ }
else throw e;
} Prevention
- Define action names as shared constants copied exactly from the API docs.
- Validate request bodies with a schema before sending.
- Version-check client code against the Flowable REST API version in use.
When it happens
Trigger: POST /management/deadletter-jobs with a BulkMoveDeadLetterActionRequest whose action is null, misspelled (e.g. 'Move', 'retry'), or otherwise not exactly 'move' or 'moveToHistoryJob'.
Common situations: Custom tooling that guesses the action name, older clients built before moveToHistoryJob existed, or sending an empty body (actionRequest == null).
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
- Id cannot be null.
- Id cannot be null.
- Invalid action, only 'move' or 'moveToHistoryJob' is…
- Invalid action, only 'move' or 'reschedule' are supported.
- UserId cannot be null.
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/fbb97ca092bbd9fe.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-rest/src/main/java/org/flowable/rest/service/api/management/JobCollectionResource.java:201
}
if (restApiInterceptor != null) {
restApiInterceptor.accessJobInfoWithQuery(query);
}
return paginateList(allRequestParams, query, "id", JobQueryProperties.PROPERTIES, restResponseFactory::createJobResponseList);
}
@ApiOperation(value = "Move a bulk of deadletter jobs. Accepts 'move' and 'moveToHistoryJob' as action.", tags = { "Jobs" }, code = 204)
@ApiResponses(value = {
@ApiResponse(code = 204, message = "Indicates the dead letter jobs where moved. Response-body is intentionally empty."),
@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("/management/deadletter-jobs")
@ResponseStatus(value = HttpStatus.NO_CONTENT)
public void executeDeadLetterJobAction(@RequestBody BulkMoveDeadLetterActionRequest 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.");
}
List<String> jobIds = actionRequest.getJobIds();
long existingJobIdCount = managementService.createDeadLetterJobQuery().jobIds(jobIds).count();
if (jobIds.size() != existingJobIdCount) {
List<Job> foundJobs = managementService.createDeadLetterJobQuery().jobIds(jobIds).list();
for (Job job : foundJobs) {
jobIds.remove(job.getId());
}
throw new FlowableObjectNotFoundException(
"Could not find a dead letter job(s) with id(s) {" + jobIds.stream().collect(Collectors.joining(",")) + "}", Job.class);
}
if (MOVE_ACTION.equals(actionRequest.getAction())) {
if (restApiInterceptor != null) {
restApiInterceptor.bulkMoveDeadLetterJobs(actionRequest.getJobIds(), MOVE_ACTION);
}
managementService.bulkMoveDeadLetterJobs(jobIds, processEngineConfiguration.getAsyncExecutorNumberOfRetries());
} else if (MOVE_TO_HISTORY_JOB_ACTION.equals(actionRequest.getAction())) {View on GitHub (pinned to d6d39ce1c6)