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

  1. Set the job id captured at acquisition: completionBuilder.externalJobId(job.getId()).
  2. Validate the job id is non-empty in the worker before invoking the command.
  3. 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

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


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