{"record":{"id":"ce4cc74866db3065","repo":"flowable/flowable-engine","slug":"could-not-find-a-dead-letter-job-s-with-id-s","errorCode":null,"errorMessage":"Could not find a dead letter job(s) with id(s) {}","messagePattern":"Could not find a dead letter job\\(s\\) with id\\(s\\) (.+?)","errorType":"http","errorClass":"FlowableObjectNotFoundException","httpStatus":404,"severity":"error","filePath":"modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/rest/service/api/management/JobCollectionResource.java","lineNumber":210,"sourceCode":"    @ApiResponses(value = {\n            @ApiResponse(code = 204, message = \"Indicates the dead letter jobs where moved. Response-body is intentionally empty.\"),\n            @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.\")\n    })\n    @ResponseStatus(value = HttpStatus.NO_CONTENT)\n    @PostMapping(\"/cmmn-management/deadletter-jobs\")\n    public void executeDeadLetterJobAction(@RequestBody BulkMoveDeadLetterActionRequest actionRequest) {\n        if (actionRequest == null || !(MOVE_ACTION.equals(actionRequest.getAction()) || MOVE_TO_HISTORY_JOB_ACTION.equals(actionRequest.getAction()))) {\n            throw new FlowableIllegalArgumentException(\"Invalid action, only 'move' or 'moveToHistoryJob' is supported.\");\n        }\n\n        Collection<String> jobIds = actionRequest.getJobIds();\n        long existingJobIdCount = managementService.createDeadLetterJobQuery().jobIds(jobIds).count();\n        if (jobIds.size() != existingJobIdCount) {\n            List<Job> foundJobs = managementService.createDeadLetterJobQuery().jobIds(jobIds).list();\n            for (Job job : foundJobs) {\n                jobIds.remove(job.getId());\n            }\n            throw new FlowableObjectNotFoundException(\n                    \"Could not find a dead letter job(s) with id(s) {\" + jobIds.stream().collect(Collectors.joining(\",\")) + \"}\", Job.class);\n        }\n        if (MOVE_ACTION.equals(actionRequest.getAction())) {\n            if (restApiInterceptor != null) {\n                restApiInterceptor.bulkMoveDeadLetterJobs(actionRequest.getJobIds(), MOVE_ACTION);\n            }\n            managementService.bulkMoveDeadLetterJobs(actionRequest.getJobIds(), cmmnEngineConfiguration.getAsyncExecutorNumberOfRetries());\n        } else if (MOVE_TO_HISTORY_JOB_ACTION.equals(actionRequest.getAction())) {\n            if (restApiInterceptor != null) {\n                restApiInterceptor.bulkMoveDeadLetterJobs(actionRequest.getJobIds(), MOVE_TO_HISTORY_JOB_ACTION);\n            }\n            managementService.bulkMoveDeadLetterJobsToHistoryJobs(actionRequest.getJobIds(), cmmnEngineConfiguration.getAsyncHistoryExecutorNumberOfRetries());\n        }\n    }\n}\n","sourceCodeStart":192,"sourceCodeEnd":226,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/rest/service/api/management/JobCollectionResource.java#L192-L226","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n{\"action\":\"move\",\"jobIds\":[\"1\",\"2\",\"stale-id\"]}\n// after\n{\"action\":\"move\",\"jobIds\":[\"1\",\"2\"]} // ids refreshed from deadletter-jobs list","handlingStrategy":"validation","validationCode":"const ids = (await listDeadLetterJobs()).map(j => j.id);\nconst requested = ids.filter(id => wanted.has(id));\nif (requested.length === 0) throw new Error(\"no valid dead letter job ids to move\");","typeGuard":null,"tryCatchPattern":"try { bulkMove(ids) } catch (e) { if (e.status === 404) { retryWith(parseMissingIds(e.message)); } else throw e; }","preventionTips":["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."],"tags":["rest-api","resource-not-found","jobs"],"backgroundTag":"record-not-found","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}