flowable/flowable-engine · error · FlowableObjectNotFoundException
Could not find a dead letter job(s) with id(s) {}
Error message
Could not find a dead letter job(s) with id(s) {} What it means
After validating the action, executeDeadLetterJobAction counts the requested job ids among dead letter jobs; if some ids don't exist it removes found ids and throws FlowableObjectNotFoundException listing the missing id(s).
Source
Thrown at modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/rest/service/api/management/JobCollectionResource.java:210
@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.")
})
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping("/cmmn-management/deadletter-jobs")
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.");
}
Collection<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(actionRequest.getJobIds(), cmmnEngineConfiguration.getAsyncExecutorNumberOfRetries());
} else if (MOVE_TO_HISTORY_JOB_ACTION.equals(actionRequest.getAction())) {
if (restApiInterceptor != null) {
restApiInterceptor.bulkMoveDeadLetterJobs(actionRequest.getJobIds(), MOVE_TO_HISTORY_JOB_ACTION);
}
managementService.bulkMoveDeadLetterJobsToHistoryJobs(actionRequest.getJobIds(), cmmnEngineConfiguration.getAsyncHistoryExecutorNumberOfRetries());
}
}
}
View on GitHub (pinned to d6d39ce1c6)
Solutions
- Re-query /cmmn-management/deadletter-jobs and build jobIds only from the current response.
- Parse the error message to remove the missing ids and retry with valid ones only.
- Make the move idempotent: treat 'job not found' for already-moved jobs as success.
Example fix
// before
{"action":"move","jobIds":["1","2","stale-id"]}
// after
{"action":"move","jobIds":["1","2"]} // ids refreshed from deadletter-jobs list Defensive patterns
Strategy: validation
Validate before calling
const ids = (await listDeadLetterJobs()).map(j => j.id);
const requested = ids.filter(id => wanted.has(id));
if (requested.length === 0) throw new Error("no valid dead letter job ids to move"); Try / catch
try { bulkMove(ids) } catch (e) { if (e.status === 404) { retryWith(parseMissingIds(e.message)); } else throw e; } Prevention
- Build jobIds from a fresh dead letter job listing.
- Parse the missing ids out of the error message for partial retries.
- Make bulk moves idempotent — already-moved jobs surface as 404.
When it happens
Trigger: POST /cmmn-management/deadletter-jobs with jobIds containing at least one id that is not (or no longer) a dead letter job — mixed job types, already-moved jobs, typos, or duplicate/empty ids.
Common situations: Bulk-move built from a cached job list where some jobs were already handled; copying ids from BPM engine lists into CMMN requests; retrying a move that partially succeeded earlier.
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 job with id ''.
- Could not find a history job with id ''.
- Job with id '' doesn't have an exception stacktrace.
- The variable does not have a binary data stream.
- Invalid action, only 'move' or 'moveToHistoryJob' is support
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/ce4cc74866db3065.
Report an issue: GitHub.