flowable/flowable-engine · error · FlowableIllegalArgumentException

jobId is null

Error message

jobId is null

What it means

ExecuteAsyncJobCmd throws FlowableIllegalArgumentException when its jobId constructor argument is null. Asynchronous job execution is keyed purely on the id, so a null id makes the command meaningless and is rejected up front.

Source

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

    }
    
    public ExecuteAsyncJobCmd(String jobId, JobInfoEntityManager<? extends JobInfoEntity> jobEntityManager,
            JobServiceConfiguration jobServiceConfiguration) {
        
        this.jobId = jobId;
        this.jobEntityManager = jobEntityManager;
        this.jobServiceConfiguration = jobServiceConfiguration;
    }

    @Override
    public Object execute(CommandContext commandContext) {
        
        if (jobEntityManager == null) {
            jobEntityManager = jobServiceConfiguration.getJobEntityManager(); // Backwards compatibility
        }

        if (jobId == null) {
            throw new FlowableIllegalArgumentException("jobId is null");
        }

        // We need to refetch the job, as it could have been deleted by another concurrent job
        // For example: an embedded subprocess with a couple of async tasks and a timer on the boundary of the subprocess
        // when the timer fires, all executions and thus also the jobs inside of the embedded subprocess are destroyed.
        // However, the async task jobs could already have been fetched and put in the queue.... while in reality they have been deleted.
        // A refetch is thus needed here to be sure that it exists for this transaction.

        JobInfoEntity job = jobEntityManager.findById(jobId);
        if (job == null) {
            LOGGER.debug("Job does not exist anymore and will not be executed. It has most likely been deleted "
                    + "as part of another concurrent part of the process instance.");
            return null;
        }

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Executing async job {}", job.getId());
        }

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Ensure the id passed to the command/executor API is non-null before dispatching.
  2. If the id comes from a queued message, guard the deserialization step so null ids are dropped with a log line.
  3. Check custom executor wrappers that may pass a variable initialized to null.
  4. Add an assert/early return in the code that builds the command.

Example fix

// before
commandExecutor.execute(new ExecuteAsyncJobCmd(jobId)); // jobId may be null
// after
Objects.requireNonNull(jobId, "jobId must not be null");
commandExecutor.execute(new ExecuteAsyncJobCmd(jobId));
Defensive patterns

Strategy: validation

Validate before calling

if (jobId == null || jobId.isBlank()) { throw new IllegalArgumentException("jobId required for ExecuteAsyncJobCmd"); }

Try / catch

try { commandExecutor.execute(new ExecuteAsyncJobCmd(jobId)); } catch (FlowableIllegalArgumentException e) { LOGGER.error("Rejected async job execution: {}", e.getMessage()); }

Prevention

When it happens

Trigger: Executing new ExecuteAsyncJobCmd(null) via the command executor, or async executor continuation paths passing a null job id after a fetch/dequeue produced nothing.

Common situations: Custom job executor code enqueuing jobs where the id field was never set; wrapper APIs forwarding an optional id that the caller omitted; tests invoking the command directly without arguments.

Related errors


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