{"record":{"id":"9f1899b7e0308cc6","repo":"flowable/flowable-engine","slug":"can-only-complete-bpmn-external-job-with-a-bpmn-er","errorCode":null,"errorMessage":"Can only complete BPMN external job with a BPMN error. Job with id '${jobId}' is from scope '${scopeType}'","messagePattern":"Can only complete BPMN external job with a BPMN error\\. Job with id '(.+?)' is from scope '(.+?)'","errorType":"validation","errorClass":"FlowableIllegalArgumentException","httpStatus":400,"severity":"error","filePath":"modules/flowable-external-job-rest/src/main/java/org/flowable/external/job/rest/service/api/acquire/ExternalWorkerAcquireJobResource.java","lineNumber":178,"sourceCode":"\n        if (!workerId.equals(job.getLockOwner())) {\n            throw new FlowableForbiddenException(workerId + \" does not hold a lock on the requested job\");\n        }\n\n        if (job.getProcessInstanceId() != null) {\n            if (managementService != null) {\n                if (restApiInterceptor != null) {\n                    restApiInterceptor.bpmnErrorExternalWorkerJob(job, request);\n                }\n\n                managementService.createExternalWorkerCompletionBuilder(job.getId(), workerId)\n                        .variables(extractVariables(request.getVariables()))\n                        .bpmnError(request.getErrorCode());\n            } else {\n                throw new FlowableException(\"Cannot complete BPMN job. There is no BPMN engine available\");\n            }\n        } else {\n            throw new FlowableIllegalArgumentException(\n                    \"Can only complete BPMN external job with a BPMN error. Job with id '\" + jobId + \"' is from scope '\" + job.getScopeType() + \"'\");\n        }\n\n        return ResponseEntity.noContent().build();\n    }\n\n    @ApiOperation(value = \"Complete an External Worker Job with a cmmn terminate transition\", code = 204, tags = { \"Acquire and Execute\" })\n    @ApiResponses({\n            @ApiResponse(code = 204, message = \"Indicates the job was successfully transitioned.\"),\n            @ApiResponse(code = 400, message = \"Indicates the request was invalid.\"),\n            @ApiResponse(code = 403, message = \"Indicates the user does not have the rights complete the job.\"),\n            @ApiResponse(code = 404, message = \"Indicates the job does not exist.\"),\n    })\n    @PostMapping(value = \"/acquire/jobs/{jobId}/cmmnTerminate\", produces = \"application/json\")\n    public ResponseEntity<?> terminateCmmnJob(@PathVariable String jobId, @RequestBody ExternalWorkerJobTerminateRequest request) {\n        String workerId = request.getWorkerId();\n        if (StringUtils.isEmpty(workerId)) {\n            throw new FlowableIllegalArgumentException(\"workerId is required\");","sourceCodeStart":160,"sourceCodeEnd":196,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-external-job-rest/src/main/java/org/flowable/external/job/rest/service/api/acquire/ExternalWorkerAcquireJobResource.java#L160-L196","documentation":"FlowableIllegalArgumentException thrown when a BPMN error is reported for an external worker job whose scopeType is not BPMN (e.g. a CMMN case job). BPMN errors can only be applied to jobs belonging to a BPMN process instance; CMMN jobs must be terminated via the cmmnTerminate endpoint.","triggerScenarios":"POST /acquire/jobs/{jobId}/bpmnError with a jobId that resolves to a job whose getScopeType() returns 'cmmn' (or any non-BPMN scope).","commonSituations":"Client code that acquires all external jobs indiscriminately and applies bpmnError to every failure; a mixed BPMN+CMMN deployment where the worker doesn't branch on scope type; job id mix-ups when multiple jobs are in flight.","solutions":["Check job.getScopeType() (available on the acquired ExternalWorkerJob) and call cmmnTerminate for CMMN jobs, bpmnError only for BPMN jobs.","If both engines are used, restrict the acquire request or filter acquired jobs by scope type in the worker.","Verify you are not mixing up jobIds between concurrently processed BPMN and CMMN jobs."],"exampleFix":"// before\ncompletionBuilder.bpmnError(errorCode);\n\n// after\nif (ScopeTypes.BPMN.equals(job.getScopeType())) {\n    completionBuilder.bpmnError(errorCode);\n} else {\n    cmmnManagementService.createCmmnExternalWorkerTransitionBuilder(job.getId(), workerId).terminate();\n}","handlingStrategy":"validation","validationCode":"if (job.scopeType !== 'bpmn') {\n  throw new Error('job ' + job.id + ' is scope ' + job.scopeType + '; use cmmnTerminate instead');\n}","typeGuard":"function isBpmnJob(job) { return job?.scopeType === 'bpmn'; }","tryCatchPattern":"try {\n  await api.bpmnError(jobId, req);\n} catch (e) {\n  if (e.status === 400 && /scope/.test(e.message)) {\n    // wrong endpoint for this job's scope; fetch job and route accordingly\n    return routeByScope(jobId, req);\n  }\n  throw e;\n}","preventionTips":["Check scopeType on every acquired job before choosing bpmnError vs cmmnTerminate.","In mixed BPMN+CMMN deployments, dispatch by scope type in one shared routing function.","Avoid acquiring job types your worker cannot handle."],"tags":["rest","bpmn","cmmn","scope-mismatch","external-worker"],"backgroundTag":"unsupported-operation","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}