flowable/flowable-engine · error · FlowableObjectNotFoundException

Could not find a job with id '${jobId}'.

Error message

Could not find a job with id '${jobId}'.

What it means

Thrown by POST /cmmn-management/jobs/{jobId} when the action is valid ('execute') but managementService.executeJob cannot find the job, so the REST layer re-throws FlowableObjectNotFoundException with the uniform message and Job.class type for a consistent 404.

Source

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

    @ApiResponses(value = {
            @ApiResponse(code = 204, message = "Indicates the job 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("/cmmn-management/jobs/{jobId}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void executeJobAction(@ApiParam(name = "jobId") @PathVariable String jobId, @RequestBody RestActionRequest actionRequest) {
        if (actionRequest == null || !EXECUTE_ACTION.equals(actionRequest.getAction())) {
            throw new FlowableIllegalArgumentException("Invalid action, only 'execute' is supported.");
        }
        
        Job job = getJobById(jobId);

        try {
            managementService.executeJob(job.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("/cmmn-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);

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Re-check the job exists with createJobQuery().jobId(jobId) and re-fetch fresh ids right before executing.
  2. Handle 404 as 'already executed elsewhere' and continue rather than failing.
  3. Pause the async executor during manual job administration, or check the job's lockOwner/lockExpiration to see if the executor owns it.

Example fix

// before
Job job = jobsFromOldList.get(0);
post("/cmmn-management/jobs/" + job.getId(), {"action":"execute"});
// after
Job job = managementService.createJobQuery().jobId(id).singleResult();
if (job != null) {
    post("/cmmn-management/jobs/" + job.getId(), {"action":"execute"});
}
Defensive patterns

Strategy: try-catch

Validate before calling

Job j = managementService.createJobQuery().jobId(jobId).singleResult();
boolean safe = j != null && j.getLockOwner() == null;

Try / catch

try { managementService.executeJob(jobId); }
catch (FlowableObjectNotFoundException e) { /* executor already ran it — continue */ }

Prevention

When it happens

Trigger: POST /cmmn-management/jobs/{jobId} (action=execute) where the job was already acquired and executed by the async executor between listing and the call, or the id is not a plain job id.

Common situations: Manually executing jobs while the async executor is also running (executor wins the race); id from timer/deadletter tables passed to the jobs endpoint; stale job list in an admin UI.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


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