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
- Re-check the job exists with createJobQuery().jobId(jobId) and re-fetch fresh ids right before executing.
- Handle 404 as 'already executed elsewhere' and continue rather than failing.
- 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
- Fetch job ids immediately before executing
- Check lockOwner/lockExpiration to avoid racing the async executor
- Treat 404 as 'already executed' in admin flows
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
- Could not find a case instance with id '${caseInstanceId}'.
- Historic task instance '' variable value for couldn't be fo
- Historic variable instance '' couldn't be found.
- Timer job with id '' doesn't have an exception stacktrace.
- Suspended job with id '' doesn't have an exception stacktrac
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/58152723bb17de3e.
Report an issue: GitHub.