{"record":{"id":"09ca07b2061f67bd","repo":"flowable/flowable-engine","slug":"cannot-move-the-job-to-deadletter-the-job-is-not","errorCode":null,"errorMessage":"Cannot move the job to deadletter: the job is not a timer, async job or external worker job","messagePattern":"Cannot move the job to deadletter: the job is not a timer, async job or external worker job","errorType":"validation","errorClass":"FlowableIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/asyncexecutor/DefaultJobManager.java","lineNumber":248,"sourceCode":"        }\n\n        jobServiceConfiguration.getSuspendedJobEntityManager().delete(job);\n        return activatedJob;\n    }\n\n    @Override\n    public DeadLetterJobEntity moveJobToDeadLetterJob(AbstractRuntimeJobEntity job) {\n        DeadLetterJobEntity deadLetterJob = createDeadLetterJobFromOtherJob(job);\n        jobServiceConfiguration.getDeadLetterJobEntityManager().insert(deadLetterJob);\n        if (job instanceof TimerJobEntity) {\n            jobServiceConfiguration.getTimerJobEntityManager().delete((TimerJobEntity) job);\n\n        } else if (job instanceof JobEntity) {\n            jobServiceConfiguration.getJobEntityManager().delete((JobEntity) job);\n        } else if (job instanceof ExternalWorkerJobEntity) {\n            jobServiceConfiguration.getExternalWorkerJobEntityManager().delete((ExternalWorkerJobEntity) job);\n        } else {\n            throw new FlowableIllegalArgumentException(\"Cannot move the job to deadletter: the job is not a timer, async job or external worker job\");\n        }\n\n        return deadLetterJob;\n    }\n\n    protected void sendMoveToDeadletterEvent(JobInfo job) {\n        FlowableEventDispatcher eventDispatcher = jobServiceConfiguration.getEventDispatcher();\n        if (eventDispatcher != null && eventDispatcher.isEnabled()) {\n            eventDispatcher.dispatchEvent(FlowableJobEventBuilder.createEntityEvent(\n                FlowableEngineEventType.JOB_MOVED_TO_DEADLETTER, job), jobServiceConfiguration.getEngineName());\n        }\n    }\n\n    @Override\n    public Job moveDeadLetterJobToExecutableJob(DeadLetterJobEntity deadLetterJobEntity, int retries) {\n        if (deadLetterJobEntity == null) {\n            throw new FlowableIllegalArgumentException(\"Null job provided\");\n        }","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/asyncexecutor/DefaultJobManager.java#L230-L266","documentation":"moveJobToDeadLetterJob moves a timer, async, or external-worker job to the dead-letter table after repeated failures. If the job object is none of the recognized types (TimerJobEntity, JobEntity, ExternalWorkerJobEntity, or their dead-letter variants handled earlier), the library throws FlowableIllegalArgumentException because dead-lettering that job type is unsupported.","triggerScenarios":"Calling moveJobToDeadLetterJob with a HistoryJobEntity, suspended-job wrapper, or other JobInfo subtype not handled by the instanceof chain; passing a custom Job implementation.","commonSituations":"Custom failure-handling code that forwards every failed job to the dead-letter path without filtering by type; framework upgrades adding new job types that old handler code funnels into moveJobToDeadLetterJob.","solutions":["Filter by job type before calling: only pass timer, async (JobEntity), or external-worker jobs","Handle history jobs with moveDeadLetterJobToHistoryJob or dedicated history-job APIs instead","Update custom failure handlers when new Flowable job types are introduced"],"exampleFix":"// before\njobManager.moveJobToDeadLetterJob(job); // job may be a history job\n// after\nif (job instanceof TimerJobEntity || job instanceof JobEntity || job instanceof ExternalWorkerJobEntity) {\n    jobManager.moveJobToDeadLetterJob(job);\n} else {\n    // route other job types appropriately\n}","handlingStrategy":"type-guard","validationCode":"if (!(job instanceof TimerJobEntity) && !(job instanceof JobEntity) && !(job instanceof ExternalWorkerJobEntity)) { throw new IllegalArgumentException(\"Unsupported job type for deadletter: \" + job.getClass()); }","typeGuard":"boolean canMoveToDeadLetter(JobInfo job) { return job instanceof TimerJobEntity || job instanceof JobEntity || job instanceof ExternalWorkerJobEntity; }","tryCatchPattern":"try { jobManager.moveJobToDeadLetterJob(job); } catch (FlowableIllegalArgumentException e) { log.error(\"Job type not supported for deadletter: {}\", job.getClass(), e); }","preventionTips":["Filter by job type before dead-lettering","Keep failure handlers in sync with Flowable job types","Route history jobs to their dedicated APIs"],"tags":["flowable","job-scheduling","type-mismatch","deadletter"],"backgroundTag":"unsupported-operation","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-18T11:17:12.947Z"}