flowable/flowable-engine · error · FlowableIllegalArgumentException

Invalid action, only 'move' or 'reschedule' are supported.

Error message

Invalid action, only 'move' or 'reschedule' are supported.

What it means

FlowableIllegalArgumentException thrown by the REST endpoint POST /management/timer-jobs/{jobId}. This endpoint executes a management action on a timer job, and only the actions 'move' and 'reschedule' are recognized. It is thrown when the request body is missing or its 'action' field is anything other than those two values, before any job lookup happens.

Solutions

  1. Set the request body {"action":"move"} to move the timer job to the executable job table
  2. Set the request body {"action":"reschedule","dueDate":"<ISO date>"} to reschedule the timer job
  3. Fix the 'action' value casing/spelling — the comparison is case-sensitive against MOVE_ACTION and RESCHEDULE_ACTION constants
  4. Ensure the POST sends Content-Type: application/json and a non-empty body

Example fix

// before
curl -X POST .../management/timer-jobs/123 -d '{"action":"rescheduleTimeDate"}'
// after
curl -X POST .../management/timer-jobs/123 -H 'Content-Type: application/json' -d '{"action":"reschedule","dueDate":"2026-09-12T10:00:00Z"}'
Defensive patterns

Strategy: validation

Validate before calling

const ACTIONS = ['move', 'reschedule'];
if (!body || !ACTIONS.includes(body.action)) throw new Error("action must be 'move' or 'reschedule'");
if (body.action === 'reschedule' && !body.dueDate) throw new Error('reschedule requires dueDate');

Type guard

function isTimerJobAction(a) { return a === 'move' || a === 'reschedule'; }

Try / catch

try { await post(`/management/timer-jobs/${id}`, body); } catch (e) { if (e.status === 400) fixPayload(e); else throw e; }

Prevention

When it happens

Trigger: POST to /flowable-rest/management/timer-jobs/{jobId} with (a) no/empty request body, (b) a body without an 'action' field, or (c) action set to any string other than 'move' or 'reschedule' (e.g. 'Move', 'rescheduleTimeDate', 'delete').

Common situations: Copy-pasting job-action code from the deadletter-jobs endpoint (which uses 'moveToHistoryJob'), typos or wrong casing in the action string, forgetting the JSON body entirely, or using an old client built for a different Flowable REST action vocabulary.

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


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

Appendix: source

Thrown at modules/flowable-rest/src/main/java/org/flowable/rest/service/api/management/JobResource.java:284

        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 = "Execute a single job action (move or reschedule)", tags = { "Jobs" }, code = 204)
    @ApiResponses(value = {
            @ApiResponse(code = 204, message = "Indicates the timer job action was executed. 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("/management/timer-jobs/{jobId}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void executeTimerJobAction(@ApiParam(name = "jobId") @PathVariable String jobId, @RequestBody TimerJobActionRequest actionRequest) {
        if (actionRequest == null || !(MOVE_ACTION.equals(actionRequest.getAction()) || RESCHEDULE_ACTION.equals(actionRequest.getAction()))) {
            throw new FlowableIllegalArgumentException("Invalid action, only 'move' or 'reschedule' are supported.");
        }
        
        Job job = getTimerJobById(jobId);

        if (MOVE_ACTION.equals(actionRequest.getAction())) {
            try {
                managementService.moveTimerToExecutableJob(job.getId());
            } catch (FlowableObjectNotFoundException e) {
                // Re-throw to have consistent error-messaging across REST-api
                throw new FlowableObjectNotFoundException("Could not find a timer job with id '" + jobId + "'.", Job.class);
            }
        } else if (RESCHEDULE_ACTION.equals(actionRequest.getAction())) {
            if (actionRequest.getDueDate() == null) {
                throw new FlowableIllegalArgumentException("Invalid reschedule timer action. Reschedule timer actions must have a valid due date.");
            }
            try {
                managementService.rescheduleTimeDateJob(job.getId(), actionRequest.getDueDate());
            } catch (FlowableObjectNotFoundException e) {

View on GitHub (pinned to d6d39ce1c6)