flowable/flowable-engine · error · FlowableIllegalArgumentException
externalJobId must not be empty
Error message
externalJobId must not be empty
What it means
FlowableIllegalArgumentException thrown by resolveJob in AbstractExternalWorkerJobCmd when the externalJobId supplied to the command (complete/acquire/failure) is null or empty. The command cannot locate the job without an id.
Source
Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/cmd/AbstractExternalWorkerJobCmd.java:74
CmmnEngineConfiguration cmmnEngineConfiguration = CommandContextUtil.getCmmnEngineConfiguration(commandContext);
new UnlockExclusiveJobCmd(externalWorkerJob, cmmnEngineConfiguration.getJobServiceConfiguration()).execute(commandContext);
}
return null;
}
protected abstract void runJobLogic(ExternalWorkerJobEntity externalWorkerJob, CommandContext commandContext);
protected void moveExternalWorkerJobToExecutableJob(ExternalWorkerJobEntity externalWorkerJob, CommandContext commandContext) {
CmmnEngineConfiguration cmmnEngineConfiguration = CommandContextUtil.getCmmnEngineConfiguration(commandContext);
cmmnEngineConfiguration.getJobServiceConfiguration().getJobManager().moveExternalWorkerJobToExecutableJob(externalWorkerJob);
cmmnEngineConfiguration.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");
}
CmmnEngineConfiguration cmmnEngineConfiguration = CommandContextUtil.getCmmnEngineConfiguration(commandContext);
ExternalWorkerJobEntityManager externalWorkerJobEntityManager = cmmnEngineConfiguration.getJobServiceConfiguration().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
- Set the job id captured at acquisition: completionBuilder.externalJobId(job.getId()).
- Validate the job id is non-empty in the worker before invoking the command.
- Fix serialization of the acquisition result so the id survives to the completion step.
Example fix
// before cmmnExternalWorkerServiceProvider.completeBuilder(workerId, null).complete(); // after cmmnExternalWorkerServiceProvider.completeBuilder(workerId, acquiredJob.getId()).complete();
Defensive patterns
Strategy: validation
Validate before calling
if (externalJobId == null || externalJobId.isBlank())
throw new IllegalArgumentException("externalJobId required before completing a job"); Try / catch
try { completeBuilder(workerId, externalJobId).complete(); }
catch (FlowableIllegalArgumentException e) { if (e.getMessage().equals("externalJobId must not be empty")) { reacquireJob(); } } Prevention
- Carry the job id from the acquire result through the whole worker transaction
- Fail fast in the worker when deserialized job payloads lack an id
When it happens
Trigger: Constructing an ExternalWorkerJobCompletionBuilder / failure command without calling externalJobId(...), or passing an empty string variable from the worker integration layer.
Common situations: Job id lost between acquire and complete phases (e.g., deserialized payload missing the id field); template code with placeholder id never filled in.
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
- There can only be one reactivation listener on a case model,
- A 'maxInstanceCount' on a repetition rule with value '0' is
- Job {jobId} parent is not CMMN case
- no topic expression configured for
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/fc1f008594e50497.
Report an issue: GitHub.