flowable/flowable-engine · error · FlowableIllegalArgumentException
Can only terminate CMMN external job. Job with id '${jobId}'
Error message
Can only terminate CMMN external job. Job with id '${jobId}' is from scope '${scopeType}' What it means
FlowableIllegalArgumentException thrown when termination is requested for an external worker job whose scopeType is not CMMN (typically a BPMN process job). CMMN termination transitions apply only to CMMN case jobs; BPMN jobs must be completed or failed via their own endpoints. The message includes the offending job id and actual scope type.
Source
Thrown at modules/flowable-external-job-rest/src/main/java/org/flowable/external/job/rest/service/api/acquire/ExternalWorkerAcquireJobResource.java:218
if (!workerId.equals(job.getLockOwner())) {
throw new FlowableForbiddenException(workerId + " does not hold a lock on the requested job");
}
if (ScopeTypes.CMMN.equals(job.getScopeType())) {
if (cmmnManagementService != null) {
if (restApiInterceptor != null) {
restApiInterceptor.cmmnTerminateExternalWorkerJob(job, request);
}
cmmnManagementService.createCmmnExternalWorkerTransitionBuilder(job.getId(), workerId)
.variables(extractVariables(request.getVariables()))
.terminate();
} else {
throw new FlowableException("Cannot complete CMMN job. There is no CMMN engine available");
}
} else {
throw new FlowableIllegalArgumentException(
"Can only terminate CMMN external job. Job with id '" + jobId + "' is from scope '" + job.getScopeType() + "'");
}
return ResponseEntity.noContent().build();
}
@ApiOperation(value = "Fail an External Worker Job", code = 204, tags = { "Acquire and Execute" })
@ApiResponses({
@ApiResponse(code = 204, message = "Indicates the job was successfully completed."),
@ApiResponse(code = 400, message = "Indicates the request was invalid."),
@ApiResponse(code = 403, message = "Indicates the user does not have the rights complete the job."),
@ApiResponse(code = 404, message = "Indicates the job does not exist."),
})
@PostMapping(value = "/acquire/jobs/{jobId}/fail", produces = "application/json")
public ResponseEntity<?> failJob(@PathVariable String jobId, @RequestBody ExternalWorkerJobFailureRequest request) {
String workerId = request.getWorkerId();
if (StringUtils.isEmpty(workerId)) {
throw new FlowableIllegalArgumentException("workerId is required");View on GitHub (pinned to d6d39ce1c6)
Solutions
- Inspect the acquired job's scopeType and branch: use failJob/bpmnError for BPMN jobs, cmmnTerminate for CMMN jobs.
- Filter acquire requests or acquired job lists to CMMN jobs if the worker only handles cases.
- Log the jobId and scopeType on failure to catch id-mixing bugs in worker loops.
Example fix
// before
transitionBuilder.terminate();
// after
if (ScopeTypes.CMMN.equals(job.getScopeType())) {
transitionBuilder.terminate();
} else {
managementService.createExternalWorkerCompletionBuilder(job.getId(), workerId).fail(errorMessage);
} Defensive patterns
Strategy: validation
Validate before calling
if (job.scopeType !== 'cmmn') {
throw new Error('job ' + job.id + ' is scope ' + job.scopeType + '; cmmnTerminate is CMMN-only');
} Type guard
function isCmmnJob(job) { return job?.scopeType === 'cmmn'; } Try / catch
try {
await api.cmmnTerminate(jobId, req);
} catch (e) {
if (e.status === 400 && /scope/.test(e.message)) {
return routeByScope(jobId, req); // use fail/bpmnError for BPMN jobs
}
throw e;
} Prevention
- Branch on scopeType before selecting the completion endpoint.
- Never assume the scope of a job id in shared worker loops.
- Log jobId + scopeType when dispatching to ease debugging.
When it happens
Trigger: POST /acquire/jobs/{jobId}/cmmnTerminate with a jobId resolving to a job whose getScopeType() is not 'cmmn' (e.g. 'bpmn').
Common situations: Generic worker loops that call terminate for every failed job regardless of scope; mixed-engine deployments where scope type was not inspected; off-by-one/id-swap bugs in batch processing of acquired jobs.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Can only complete BPMN external job with a BPMN error. Job w
- Cannot query external jobs. There is no BPMN or CMMN engine
- ${externalWorkerJob} is not cmmn scoped. This command can on
- {externalWorkerJob} is not bpmn scoped. This command can onl
- Cannot complete BPMN job. There is no BPMN engine available
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/b45d51888400db59.
Report an issue: GitHub.