flowable/flowable-engine · error · FlowableIllegalArgumentException

Only one of user or group can be used to create an identity…

Error message

Only one of user or group can be used to create an identity link.

What it means

The Flowable CMMN REST API rejects an identity link creation request when the payload specifies both a user and a group. An identity link associates exactly one principal (a user OR a group) with a task, so a request carrying both is ambiguous and cannot be represented.

Solutions

  1. Remove the 'user' or the 'group' field from the request body so exactly one is set
  2. If the intent is a user link, set only {"user": "...", "type": "candidate"}; for a group link set only {"group": "...", "type": "candidate"}
  3. Fix client-side serialization so mutually exclusive principals never both appear in the payload

Example fix

// before
POST /cmmn-runtime/tasks/123/identitylinks
{"user":"john","group":"management","type":"candidate"}
// after
POST /cmmn-runtime/tasks/123/identitylinks
{"group":"management","type":"candidate"}
Defensive patterns

Strategy: validation

Validate before calling

function canCreateIdentityLink(body) {
  const count = [body.user, body.group].filter(Boolean).length;
  return count === 1 && typeof body.type === 'string';
}

Type guard

function hasExactlyOnePrincipal(b) {
  return (b.user != null) !== (b.group != null);
}

Try / catch

try {
  await post(`/cmmn-runtime/tasks/${taskId}/identitylinks`, body);
} catch (e) {
  if (e.status === 400 && /Only one of user or group/.test(e.body.message)) {
    throw new Error('Send either user or group, not both');
  }
  throw e;
}

Prevention

When it happens

Trigger: POSTing to /cmmn-runtime/tasks/{taskId}/identitylinks with a JSON body where both 'user' and 'group' fields are non-null.

Common situations: Client code copies an object with both fields populated; a form collects both assignee-user and candidate-group and serializes them together; a generic 'owner' object is mapped into the request body without picking one principal.

Related errors


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

Appendix: source

Thrown at modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/rest/service/api/runtime/task/TaskIdentityLinkCollectionResource.java:76

    @ApiOperation(value = "Create an identity link on a task", tags = { "Task Identity Links" }, nickname = "createTaskInstanceIdentityLinks",
            notes = "It is possible to add either a user or a group.", code = 201)
    @ApiResponses(value = {
            @ApiResponse(code = 201, message = "Indicates the task was found and the identity link was created."),
            @ApiResponse(code = 404, message = "Indicates the requested task was not found or the task does not have the requested identityLink. The status contains additional information about this error.")
    })
    @PostMapping(value = "/cmmn-runtime/tasks/{taskId}/identitylinks", produces = "application/json")
    @ResponseStatus(HttpStatus.CREATED)
    public RestIdentityLink createIdentityLink(@ApiParam(name = "taskId") @PathVariable("taskId") String taskId, @RequestBody RestIdentityLink identityLink) {

        Task task = getTaskFromRequestWithoutAccessCheck(taskId);

        if (identityLink.getGroup() == null && identityLink.getUser() == null) {
            throw new FlowableIllegalArgumentException("A group or a user is required to create an identity link.");
        }

        if (identityLink.getGroup() != null && identityLink.getUser() != null) {
            throw new FlowableIllegalArgumentException("Only one of user or group can be used to create an identity link.");
        }

        if (identityLink.getType() == null) {
            throw new FlowableIllegalArgumentException("The identity link type is required.");
        }

        if (restApiInterceptor != null) {
            restApiInterceptor.createTaskIdentityLink(task, identityLink);
        }

        if (identityLink.getGroup() != null) {
            taskService.addGroupIdentityLink(task.getId(), identityLink.getGroup(), identityLink.getType());
        } else {
            taskService.addUserIdentityLink(task.getId(), identityLink.getUser(), identityLink.getType());
        }

        return restResponseFactory.createRestIdentityLink(identityLink.getType(), identityLink.getUser(), identityLink.getGroup(), task.getId(), null, null);
    }

View on GitHub (pinned to d6d39ce1c6)