{"record":{"id":"bdb79567871f05ed","repo":"flowable/flowable-engine","slug":"externaljobid-must-not-be-empty-bdb795","errorCode":null,"errorMessage":"externalJobId must not be empty","messagePattern":"externalJobId must not be empty","errorType":"validation","errorClass":"FlowableIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/cmd/AbstractExternalWorkerJobCmd.java","lineNumber":58,"sourceCode":"\n    @Override\n    public Void execute(CommandContext commandContext) {\n        ExternalWorkerJobEntity externalWorkerJob = resolveJob();\n        runJobLogic(externalWorkerJob, commandContext);\n        if (externalWorkerJob.isExclusive()) {\n            // Part of the same transaction to avoid a race condition with the\n            // potentially new jobs (wrt process instance locking) that are created\n            // during the execution of the original job\n            new UnlockExclusiveJobCmd(externalWorkerJob, jobServiceConfiguration).execute(commandContext);\n        }\n        return null;\n    }\n\n    protected abstract void runJobLogic(ExternalWorkerJobEntity externalWorkerJob, CommandContext commandContext);\n\n    protected ExternalWorkerJobEntity resolveJob() {\n        if (StringUtils.isEmpty(externalJobId)) {\n            throw new FlowableIllegalArgumentException(\"externalJobId must not be empty\");\n        }\n\n        if (StringUtils.isEmpty(workerId)) {\n            throw new FlowableIllegalArgumentException(\"workerId must not be empty\");\n        }\n\n        ExternalWorkerJobEntityManager externalWorkerJobEntityManager = jobServiceConfiguration.getExternalWorkerJobEntityManager();\n\n        ExternalWorkerJobEntity job = externalWorkerJobEntityManager.findById(externalJobId);\n\n        if (job == null) {\n            throw new FlowableObjectNotFoundException(\"No External Worker job found for id: \" + externalJobId, ExternalWorkerJobEntity.class);\n        }\n\n        if (!Objects.equals(workerId, job.getLockOwner())) {\n            throw new FlowableIllegalArgumentException(workerId + \" does not hold a lock on the requested job\");\n        }\n","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/cmd/AbstractExternalWorkerJobCmd.java#L40-L76","documentation":"This FlowableIllegalArgumentException is thrown by resolveJob() in AbstractExternalWorkerJobCmd when the externalJobId passed to an external-worker job command (acquire/complete/terminate/fail) is null or empty. Flowable validates command inputs up front before touching the database, so the command fails fast rather than issuing a lookup with a blank identifier. It indicates the calling code never set the external job id on the builder/command.","triggerScenarios":"Calling ExternalWorkerJobCompletionBuilder-based commands (e.g. completeExternalWorkerJob, terminateExternalWorkerJob, failExternalWorkerJob) without setting externalJobId; passing an empty String from an unmarshalled payload; constructing the command directly with externalJobId == null.","commonSituations":"Deserializing a worker callback message where the job id field was dropped or empty; copy-pasting completion code that sets workerId but forgets job id; a variable holding the job id resolved to empty string from an unset process variable.","solutions":["Set the externalJobId on the ExternalWorkerJobCompletionBuilder before executing the command, e.g. externalWorkerJobCompletionBuilder.externalJobId(jobId)","Check where the id originates (message payload, process variable) and log/validate it before calling the Flowable API","If the id is genuinely unknown, re-acquire the job via ExternalWorkerJobAcquireBuilder so a valid id and lock are available"],"exampleFix":"// before\nexternalWorkerJobCompletionBuilder\n    .workerId(workerId)\n    .complete();\n// after\nexternalWorkerJobCompletionBuilder\n    .externalJobId(externalJobId)\n    .workerId(workerId)\n    .complete();","handlingStrategy":"validation","validationCode":"if (externalJobId == null || externalJobId.isEmpty()) {\n    throw new IllegalArgumentException(\"externalJobId must be set before completing the job\");\n}","typeGuard":"boolean hasJobId(String id) { return id != null && !id.trim().isEmpty(); }","tryCatchPattern":"try {\n    completionBuilder.externalJobId(externalJobId).workerId(workerId).complete();\n} catch (FlowableIllegalArgumentException e) {\n    LOGGER.error(\"Invalid external worker job command: {}\", e.getMessage());\n}","preventionTips":["Always set externalJobId on the builder before complete/fail/terminate","Validate ids at the message-queue/consumer boundary before invoking Flowable","Log the raw payload when an id is missing to trace where it is dropped"],"tags":["validation","flowable","external-worker-job"],"backgroundTag":"empty-required-field","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}