flowable/flowable-engine · error · FlowableIllegalArgumentException

Can only unacquire BPMN or CMMN external job. Job with id

Error message

Can only unacquire BPMN or CMMN external job. Job with id '${jobId}' is from scope '${scopeType}'

What it means

FlowableIllegalArgumentException thrown by unaquireJob when the job's scopeType is neither BPMN nor CMMN. Only external worker jobs tied to a process instance or case instance can be unacquired through this endpoint. The message includes the offending jobId and actual scope value.

Solutions

  1. Only unacquire jobs that were acquired through the external worker acquisition API for BPMN/CMMN scopes.
  2. Check the job's SCOPE_TYPE_ column in ACT_RU_EXTERNAL_JOB to see what scope it carries.
  3. If the job should be scoped, fix the model so the external job is created inside a process/case.
  4. Use the correct endpoint or engine API for standalone jobs if that is the intent.

Example fix

// before: attempting unacquire of a standalone job id
DELETE-style call with jobId of a scopeless external job
// after
acquire and complete/unacquire only jobs returned by the external worker acquire endpoint
Defensive patterns

Strategy: validation

Validate before calling

String scope = job.getScopeType();
if (!"bpmn".equals(scope) && !"cmmn".equals(scope)) { throw new IllegalArgumentException("Unsupported scope: " + scope); }

Type guard

boolean isBpmnOrCmmn(ExternalWorkerJob job) { return ScopeTypes.BPMN.equals(job.getScopeType()) || ScopeTypes.CMMN.equals(job.getScopeType()); }

Try / catch

try { unaquireJob(jobId, request); } catch (FlowableIllegalArgumentException e) { if (e.getMessage().contains("Can only unacquire BPMN or CMMN")) { useCorrectEndpointForJob(); } else { throw e; } }

Prevention

When it happens

Trigger: POST /external-worker-job/unacquire/jobs/{jobId} where the resolved job's scopeType is null or an unsupported value (e.g. a standalone/async external job without scope, or a custom scope type).

Common situations: Jobs created outside a process/case context; upgrading Flowable versions where scope handling changed; pointing the client at a job id from a different subsystem.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at modules/flowable-external-job-rest/src/main/java/org/flowable/external/job/rest/service/api/acquire/ExternalWorkerUnacquireJobResource.java:109

                managementService.unacquireExternalWorkerJob(jobId, workerId);
                
            } else {
                throw new FlowableException("Cannot unacquire BPMN job. There is no BPMN engine available");
            }
        } else if (ScopeTypes.CMMN.equals(job.getScopeType())) {
            if (cmmnManagementService != null) {
                if (restApiInterceptor != null) {
                    restApiInterceptor.unacquireExternalWorkerJob(job, request);
                }

                cmmnManagementService.unacquireExternalWorkerJob(jobId, workerId);
                
            } else {
                throw new FlowableException("Cannot unacquire CMMN job. There is no CMMN engine available");
            }
            
        } else {
            throw new FlowableIllegalArgumentException(
                    "Can only unacquire BPMN or CMMN external job. Job with id '" + jobId + "' is from scope '" + job.getScopeType() + "'");
        }

        return ResponseEntity.noContent().build();
    }
    
    protected void unaquireExternalWorkerJobs(String workerId, String tenantId) {
        if (managementService != null) {
            if (StringUtils.isNotEmpty(tenantId)) {
                managementService.unacquireAllExternalWorkerJobsForWorker(workerId, tenantId);
            } else {
                managementService.unacquireAllExternalWorkerJobsForWorker(workerId);
            }
            
        } else if (cmmnManagementService != null) {
            if (StringUtils.isNotEmpty(tenantId)) {
                cmmnManagementService.unacquireAllExternalWorkerJobsForWorker(workerId, tenantId);
            } else {

View on GitHub (pinned to d6d39ce1c6)