flowable/flowable-engine · error · FlowableObjectNotFoundException

No timer job found with id '

Error message

No timer job found with id '

What it means

DeleteTimerJobCmd looks up the timer job by id via TimerJobEntityManager.findById; when no row matches it throws FlowableObjectNotFoundException carrying Job.class as the type. It means the id is well-formed but no timer job with that id exists (or it no longer exists).

Source

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

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

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

        TimerJobEntity job = jobServiceConfiguration.getTimerJobEntityManager().findById(timerJobId);
        if (job == null) {
            throw new FlowableObjectNotFoundException("No timer job found with id '" + timerJobId + "'", Job.class);
        }

        // We need to check if the job was locked, ie acquired by the job acquisition thread
        // This happens if the job was already acquired, but not yet executed.
        // In that case, we can't allow to delete the job.
        if (job.getLockOwner() != null) {
            throw new FlowableException("Cannot delete " + job + " when the job is being executed. Try again later.");
        }
        return job;
    }

}

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Query the id first (managementService.createTimerJobQuery().timerJobId(id).singleResult()) and skip deletion when null.
  2. Catch FlowableObjectNotFoundException and treat the delete as already-done idempotently.
  3. Verify you are connected to the same database/engine the timer job was created in.
  4. Confirm you are not confusing job ids (ACT_RU_JOB) with timer job ids (ACT_RU_TIMER_JOB).

Example fix

// before
managementService.deleteTimerJob(timerJobId); // throws if already gone
// after
if (managementService.createTimerJobQuery().timerJobId(timerJobId).count() > 0) {
    managementService.deleteTimerJob(timerJobId);
}
Defensive patterns

Strategy: try-catch

Validate before calling

boolean exists = managementService.createTimerJobQuery().timerJobId(timerJobId).count() > 0;

Try / catch

try { managementService.deleteTimerJob(id); } catch (FlowableObjectNotFoundException e) { LOGGER.info("Timer job {} already gone", id); }

Prevention

When it happens

Trigger: managementService.deleteTimerJob(id) with an id that was already deleted, a job id (not timer job id) from the ACT_RU_JOB table, an id from another engine/database, or a stale id after the timer fired and moved/disappeared.

Common situations: Deleting a timer that already fired (timer jobs move to job/dead-letter tables or vanish when the execution ends); clustered setups where the id exists on another node's database; tests reusing recorded ids across cleanups; passing a TimerJobEntity.getId() after the entity was deleted.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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