flowable/flowable-engine · error · FlowableObjectNotFoundException
Could not find a dead letter job with id '${jobId}'.
Error message
Could not find a dead letter job with id '${jobId}'. What it means
Moving a deadletter job back to the executable queue failed because the job id does not exist. The REST layer re-throws FlowableObjectNotFoundException with the standard deadletter message for consistency.
Source
Thrown at modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/rest/service/api/management/JobResource.java:327
restApiInterceptor.moveDeadLetterJob(deadLetterJob, MOVE_ACTION);
}
/*
* Note that the jobType is checked to know which kind of move that needs to be done.
* The MOVE_TO_HISTORY_JOB_ACTION allows to specifically force the move to a history job and trigger the else part below.
*/
try {
if (HistoryJobEntity.HISTORY_JOB_TYPE.equals(deadLetterJob.getJobType())) {
managementService.moveDeadLetterJobToHistoryJob(deadLetterJob.getId(), cmmnEngineConfiguration.getAsyncExecutorNumberOfRetries());
} else {
managementService.moveDeadLetterJobToExecutableJob(deadLetterJob.getId(), cmmnEngineConfiguration.getAsyncExecutorNumberOfRetries());
}
} catch (FlowableObjectNotFoundException e) {
// Re-throw to have consistent error-messaging across REST-API
throw new FlowableObjectNotFoundException("Could not find a dead letter job with id '" + jobId + "'.", Job.class);
}
} else if (MOVE_TO_HISTORY_JOB_ACTION.equals(actionRequest.getAction())) {
if (restApiInterceptor != null) {
restApiInterceptor.moveDeadLetterJob(deadLetterJob, MOVE_TO_HISTORY_JOB_ACTION);
}
try {
managementService.moveDeadLetterJobToHistoryJob(deadLetterJob.getId(), cmmnEngineConfiguration.getAsyncHistoryExecutorNumberOfRetries());
} catch (FlowableObjectNotFoundException e) {
// Re-throw to have consistent error-messaging across REST-api
throw new FlowableObjectNotFoundException("Could not find a dead letter job with id '" + jobId + "'.", Job.class);
}
}
}
}
View on GitHub (pinned to d6d39ce1c6)
Solutions
- Re-list deadletter jobs to confirm the id is still present
- Handle 404 gracefully — the job may already have been moved
- Verify you are pointing at the correct CMMN engine configuration
Defensive patterns
Strategy: try-catch
Validate before calling
const job = await get(`/cmmn-management/deadletter-jobs/${jobId}`);
if (!job) throw new Error(`deadletter job ${jobId} not found`); Try / catch
try { await moveDeadLetterJob(jobId); }
catch (e) { if (e.status === 404 && /dead letter job/.test(e.message)) { /* idempotent skip: already moved */ } else throw e; } Prevention
- Make deadletter moves idempotent — 404 often means already processed
- Avoid concurrent workers moving the same job id
- Track processed ids in an audit table
When it happens
Trigger: POST /cmmn-management/deadletter-jobs/{jobId} with action 'move' where the deadletter job with that id is gone (already moved/retried) or never existed.
Common situations: Double-execution of a move script, id sourced from a different job table (timer/history), wrong engine database.
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
- Could not find a timer job with id '${jobId}'.
- Could not find a resource with id '<resourceName>' in deploy
- Could not find an app deployment with id '<deploymentId>
- Could not find a deployment with id '<deploymentId>'.
- Could not find an app deployment with id '<deploymentId>
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/a2cea3fa302a6273.
Report an issue: GitHub.