flowable/flowable-engine · error · FlowableIllegalArgumentException
externalJobId must not be empty
Error message
externalJobId must not be empty
What it means
resolveJob validates command inputs before touching the database. An empty/blank externalJobId makes it impossible to look up the job, so a FlowableIllegalArgumentException is thrown immediately. This is a guard against client-side mistakes when completing, failing, or unlocking an external worker job.
Source
Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/cmd/AbstractExternalWorkerJobCmd.java:75
// during the execution of the original job
new UnlockExclusiveJobCmd(externalWorkerJob, jobServiceConfiguration).execute(commandContext);
}
return null;
}
protected abstract void runJobLogic(ExternalWorkerJobEntity externalWorkerJob, CommandContext commandContext);
protected void moveExternalWorkerJobToExecutableJob(ExternalWorkerJobEntity externalWorkerJob, CommandContext commandContext) {
jobServiceConfiguration.getJobManager().moveExternalWorkerJobToExecutableJob(externalWorkerJob);
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
processEngineConfiguration.getIdentityLinkServiceConfiguration().getIdentityLinkService()
.deleteIdentityLinksByScopeIdAndType(externalWorkerJob.getCorrelationId(), ScopeTypes.EXTERNAL_WORKER);
}
protected ExternalWorkerJobEntity resolveJob(CommandContext commandContext) {
if (StringUtils.isEmpty(externalJobId)) {
throw new FlowableIllegalArgumentException("externalJobId must not be empty");
}
if (StringUtils.isEmpty(workerId)) {
throw new FlowableIllegalArgumentException("workerId must not be empty");
}
ExternalWorkerJobEntityManager externalWorkerJobEntityManager = jobServiceConfiguration.getExternalWorkerJobEntityManager();
ExternalWorkerJobEntity job = externalWorkerJobEntityManager.findById(externalJobId);
if (job == null) {
throw new FlowableObjectNotFoundException("No External Worker job found for id: " + externalJobId, ExternalWorkerJobEntity.class);
}
if (!Objects.equals(workerId, job.getLockOwner())) {
throw new FlowableIllegalArgumentException(workerId + " does not hold a lock on the requested job");
}
View on GitHub (pinned to d6d39ce1c6)
Solutions
- Pass the actual acquired job's id: new CompleteExternalWorkerJobCmd(job.getId(), workerId, variables).
- Validate the job id is non-empty in your worker client before building the command.
- Check that the acquisition result object is not null before reading its id.
Example fix
// before
new CompleteExternalWorkerJobCmd(jobId, workerId, variables); // jobId may be null
// after
Objects.requireNonNull(jobId, "externalJobId required");
if (jobId.isBlank()) throw new IllegalArgumentException("jobId required");
new CompleteExternalWorkerJobCmd(jobId, workerId, variables); Defensive patterns
Strategy: validation
Validate before calling
if (jobId == null || jobId.isBlank()) {
throw new IllegalArgumentException("externalJobId required");
} Prevention
- Validate job ids as soon as jobs are acquired
- Avoid null job id fields in worker DTOs
- Log acquired job ids to trace id loss in mapping code
When it happens
Trigger: Invoking any external worker job command (complete/fail/release/unlock) constructed with externalJobId = null or "".
Common situations: Job id lost when mapping an acquired job DTO back into a command; variable holding the id not initialized; copying job data between systems and dropping the id field.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- workerId must not be empty
- variableName is null
- caseInstanceId is null
- The case definition id is mandatory, but '' has been provide
- The target tenant id must not be null.
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/19c55dcebb93491f.
Report an issue: GitHub.