flowable/flowable-engine · error · FlowableIllegalArgumentException

Only one value of user or group is supported.

Error message

Only one value of user or group is supported.

What it means

An identity link may reference a user XOR a group, never both. After passing the null-check, createIdentityLink rejects requests where both fields are present (empty counts as present-and-invalid here), throwing FlowableIllegalArgumentException('Only one value of user or group is supported.').

Solutions

  1. Send only one of "user" or "group"; if you need both, issue two separate identity-link calls.
  2. Omit (don't empty-string) the unused field in your JSON body.
  3. Add client-side validation: reject payloads where both user and group are non-empty before the call.

Example fix

// before
{"type":"candidate","user":"kermit","group":"management"}
// after (two calls)
{"type":"candidate","user":"kermit"}
{"type":"candidate","group":"management"}
Defensive patterns

Strategy: validation

Validate before calling

boolean hasUser = link.getUser() != null && !link.getUser().isEmpty();
boolean hasGroup = link.getGroup() != null && !link.getGroup().isEmpty();
if (hasUser == hasGroup) {
    throw new IllegalArgumentException("Set exactly one of 'user' or 'group'");
}

Try / catch

try {
    restClient.createIdentityLink(instanceId, link);
} catch (HttpClientErrorException.BadRequest e) {
    if (e.getResponseBodyAsString().contains("Only one value of user or group")) {
        // split into two calls
    }
}

Prevention

When it happens

Trigger: POST /runtime/process-instances/{id}/identitylinks with {"type":"candidate","user":"kermit","group":"management"} — both user and group supplied. Also triggered when empty strings are sent for both fields. Thrown at ProcessInstanceIdentityLinkCollectionResource.java:80.

Common situations: Client code that always populates both DTO fields from form input; copying a user-link payload and adding a group; frameworks that send empty strings "" rather than omitting fields.

Related errors


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

Appendix: source

Thrown at modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/process/ProcessInstanceIdentityLinkCollectionResource.java:80

            notes = "Note that the groupId in Response Body will always be null, as it’s only possible to involve users with a process-instance.",
            code = 201)
    @ApiResponses(value = {
            @ApiResponse(code = 201, message = "Indicates the process instance was found and the link is created."),
            @ApiResponse(code = 400, message = "Indicates the requested body did not contain a userId or a type."),
            @ApiResponse(code = 404, message = "Indicates the requested process instance was not found.")
    })
    @PostMapping(value = "/runtime/process-instances/{processInstanceId}/identitylinks", produces = "application/json")
    @ResponseStatus(HttpStatus.CREATED)
    public RestIdentityLink createIdentityLink(@ApiParam(name = "processInstanceId") @PathVariable String processInstanceId, @RequestBody RestIdentityLink identityLink) {

        ProcessInstance processInstance = getProcessInstanceFromRequestWithoutAccessCheck(processInstanceId);

        if (identityLink.getGroup() == null && identityLink.getUser() == null) {
            throw new FlowableIllegalArgumentException("User or group are required.");
        }
        
        if (StringUtils.isEmpty(identityLink.getGroup()) && StringUtils.isEmpty(identityLink.getUser())) {
            throw new FlowableIllegalArgumentException("Only one value of user or group is supported.");
        }

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

        if (restApiInterceptor != null) {
            restApiInterceptor.createProcessInstanceIdentityLink(processInstance, identityLink);
        }

        if (StringUtils.isNotEmpty(identityLink.getGroup())) {
            runtimeService.addGroupIdentityLink(processInstance.getId(), identityLink.getGroup(), identityLink.getType());
            
        } else {
            runtimeService.addUserIdentityLink(processInstance.getId(), identityLink.getUser(), identityLink.getType());
        }

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

View on GitHub (pinned to d6d39ce1c6)