flowable/flowable-engine · error · FlowableObjectNotFoundException

No dead letter job found with id '

Error message

No dead letter job found with id '

What it means

DeleteDeadLetterJobCmd looks up a dead letter job by id via the DeadLetterJobEntityManager before deleting it. When no row exists with that id, Flowable throws FlowableObjectNotFoundException carrying the id and referencing Job.class. This is the engine telling you the delete was invoked against an id that does not exist in the ACT_RU_DEADLETTER_JOB table.

Solutions

  1. Fetch the dead letter job first (managementService.getDeadLetterJobs / createDeadLetterJobQuery().deadLetterJobId(id)) and confirm the id exists before deleting.
  2. Verify you are not deleting the same job twice (guard with an existence check or idempotent handling of FlowableObjectNotFoundException).
  3. Check the job may have been moved out of the dead letter table (retried via moveDeadLetterJobToExecutableJobCmd) — delete from its current table instead.
  4. Log the raw id and compare with the database row in ACT_RU_DEADLETTER_JOB to rule out id truncation or wrong-engine lookup.

Example fix

// before
managementService.executeCommand(new DeleteDeadLetterJobCmd(deadLetterJobId));
// after
if (managementService.createDeadLetterJobQuery().deadLetterJobId(deadLetterJobId).count() > 0) {
    managementService.executeCommand(new DeleteDeadLetterJobCmd(deadLetterJobId));
}
Defensive patterns

Strategy: validation

Validate before calling

if (deadLetterJobId == null || managementService.createDeadLetterJobQuery().deadLetterJobId(deadLetterJobId).count() == 0) {
    throw new IllegalArgumentException("Dead letter job does not exist: " + deadLetterJobId);
}

Try / catch

try {
    managementService.executeCommand(new DeleteDeadLetterJobCmd(deadLetterJobId));
} catch (FlowableObjectNotFoundException e) {
    LOGGER.info("Dead letter job {} already gone", deadLetterJobId);
}

Prevention

When it happens

Trigger: Calling ManagementService.executeCommand(new DeleteDeadLetterJobCmd(deadLetterJobId)) or an API that delegates to it (e.g. managementService.deleteJob on a dead-letter id) when the dead letter job was already deleted, never existed, or the id string is wrong/typo'd.

Common situations: Double-processing of an async failed job (one worker deletes the dead letter job, a retry path deletes it again); deleting after moving the job back to the timer/job tables; using an id from a different engine/process instance; stale UI listing after the job was resolved.

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


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

Appendix: source

Thrown at modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/cmd/DeleteDeadLetterJobCmd.java:76

    protected void sendCancelEvent(DeadLetterJobEntity jobToDelete) {
        FlowableEventDispatcher eventDispatcher = jobServiceConfiguration.getEventDispatcher();
        if (eventDispatcher != null && eventDispatcher.isEnabled()) {
            eventDispatcher.dispatchEvent(FlowableJobEventBuilder.createEntityEvent(FlowableEngineEventType.JOB_CANCELED, jobToDelete),
                    jobServiceConfiguration.getEngineName());
        }
    }

    protected DeadLetterJobEntity getJobToDelete(CommandContext commandContext) {
        if (deadLetterJobId == null) {
            throw new FlowableIllegalArgumentException("jobId is null");
        }
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Deleting job {}", deadLetterJobId);
        }

        DeadLetterJobEntity job = jobServiceConfiguration.getDeadLetterJobEntityManager().findById(deadLetterJobId);
        if (job == null) {
            throw new FlowableObjectNotFoundException("No dead letter job found with id '" + deadLetterJobId + "'", Job.class);
        }

        return job;
    }

}

View on GitHub (pinned to d6d39ce1c6)