flowable/flowable-engine · error · FlowableIllegalArgumentException

jobId is null

Error message

jobId is null

What it means

DeleteTimerJobCmd validates its timerJobId constructor argument before doing anything else, throwing FlowableIllegalArgumentException when it is null. The library requires an explicit timer job id because deletion is keyed entirely on it. This is a caller-side programming error, not a data or engine state problem.

Source

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

        TimerJobEntity jobToDelete = getJobToDelete(commandContext);

        sendCancelEvent(commandContext, jobToDelete);

        jobServiceConfiguration.getTimerJobEntityManager().delete(jobToDelete);
        return null;
    }

    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. Check that the timer job id passed to ManagementService.deleteTimerJob is non-null before calling it.
  2. If the id comes from a prior query (e.g. timer job list), verify the query actually returned a job and you took its id.
  3. Log the id source at the call site to find where the null originates.
  4. Add a unit test asserting non-null id for your delete wrapper method.

Example fix

// before
managementService.deleteTimerJob(timerJobId); // NPEs inside command
// after
if (timerJobId == null) {
    throw new IllegalArgumentException("timerJobId must be provided");
}
managementService.deleteTimerJob(timerJobId);
Defensive patterns

Strategy: validation

Validate before calling

if (timerJobId == null) { throw new IllegalArgumentException("timerJobId must not be null before deleteTimerJob"); }

Try / catch

try { managementService.deleteTimerJob(timerJobId); } catch (FlowableIllegalArgumentException e) { /* null id — fix caller */ }

Prevention

When it happens

Trigger: Calling managementService.deleteTimerJob(null) or constructing new DeleteTimerJobCmd(null) directly and executing it via the command executor.

Common situations: Java delegating code builds the command from a variable that was never assigned; callers of custom job-service code pass an id fetched from an API response that came back null; refactors changed a method signature and an id parameter is silently no longer populated.

Related errors


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