flowable/flowable-engine · error · FlowableObjectNotFoundException

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

Error message

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

What it means

When moving a suspended timer job to the executable queue, the engine could not find the given job id. Flowable re-throws the engine's FlowableObjectNotFoundException with a standardized REST message so error messaging is consistent across the REST API.

Source

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

            @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);

        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.rescheduleTimeDateValueJob(job.getId(), actionRequest.getDueDate());
            } 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);
            }
        }
    }

    @ApiOperation(value = "Execute a history job", tags = { "Jobs" }, code = 204)
    @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."),

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Verify the jobId exists by GETting the timer job or listing timer jobs first
  2. Confirm you are querying the same CMMN engine/database the job lives in
  3. Re-check that the job wasn't already moved or consumed

Example fix

// before
POST /cmmn-management/timer-jobs/unknown-id {"action":"move"}
// after
// look up a valid id first
GET /cmmn-management/timer-jobs?scopeId=myCase
then POST /cmmn-management/timer-jobs/{validId} {"action":"move"}
Defensive patterns

Strategy: try-catch

Validate before calling

const job = await get(`/cmmn-management/timer-jobs/${jobId}`);
if (!job) throw new Error(`timer job ${jobId} not found`);

Try / catch

try { await moveTimerJob(jobId); }
catch (e) { if (e.status === 404 && /Could not find a timer job/.test(e.message)) { /* refresh job list or skip */ } else throw e; }

Prevention

When it happens

Trigger: POST /cmmn-management/timer-jobs/{jobId} with action 'move' where jobId does not correspond to an existing timer job (already executed/moved, wrong engine, or never existed).

Common situations: Using a stale jobId after the timer already fired or was moved, mixing ids from the process-engine REST API with the CMMN API, or a typo in the id.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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