flowable/flowable-engine · error · FlowableIllegalArgumentException

jobId and job is null

Error message

jobId and job is null

What it means

MoveDeadLetterJobToExecutableJobCmd.execute() requires a dead-letter jobId; if it is null it throws FlowableIllegalArgumentException with the (legacy) message 'jobId and job is null'. The command moves a job out of the dead-letter table back to the executable job table, which is impossible without an id.

Solutions

  1. Null-check the dead-letter job id before invoking moveDeadLetterJobToExecutableJob.
  2. Skip or log entries with null ids in batch-retry loops instead of passing them through.
  3. Fix the source (query/UI form) that produced a null id.

Example fix

// before
managementService.moveDeadLetterJobToExecutableJob(job.getId(), 3);
// after
if (job != null && job.getId() != null) {
    managementService.moveDeadLetterJobToExecutableJob(job.getId(), 3);
}
Defensive patterns

Strategy: validation

Validate before calling

if (jobId == null || jobId.isEmpty()) throw new IllegalArgumentException("dead-letter jobId required");

Type guard

boolean movable = job != null && job.getId() != null;

Try / catch

try { managementService.moveDeadLetterJobToExecutableJob(jobId, retries); } catch (FlowableIllegalArgumentException e) { log.error("Invalid jobId: {}", e.getMessage()); }

Prevention

When it happens

Trigger: Calling ManagementService.moveDeadLetterJobToExecutableJob(jobId, ...) with a null jobId, or invoking the command programmatically without setting the id.

Common situations: Id pulled from a nullable search result or loop variable that was null for the current iteration; admin UI passes an unset field; batch retry scripts that do not filter null ids.

Related errors


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

Appendix: source

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

    private static final Logger LOGGER = LoggerFactory.getLogger(MoveDeadLetterJobToExecutableJobCmd.class);
    
    protected JobServiceConfiguration jobServiceConfiguration;

    protected String jobId;
    protected int retries;

    public MoveDeadLetterJobToExecutableJobCmd(String jobId, int retries, JobServiceConfiguration jobServiceConfiguration) {
        this.jobId = jobId;
        this.retries = retries;
        this.jobServiceConfiguration = jobServiceConfiguration;
    }

    @Override
    public Job execute(CommandContext commandContext) {

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

        DeadLetterJobEntity job = jobServiceConfiguration.getDeadLetterJobEntityManager().findById(jobId);
        if (job == null) {
            throw new JobNotFoundException(jobId);
        }

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Moving deadletter job to executable job table {}", job.getId());
        }

        return jobServiceConfiguration.getJobManager().moveDeadLetterJobToExecutableJob(job, retries);
    }

    public String getJobId() {
        return jobId;
    }

View on GitHub (pinned to d6d39ce1c6)