flowable/flowable-engine · warning · 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
Immediately after the null-check, createIdentityLink rejects bodies where BOTH 'user' and 'group' are set: a Flowable identity link is either to a user or to a group, never both. Setting both is ambiguous, so FlowableIllegalArgumentException is thrown (HTTP 400).
Source
Thrown at modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/rest/service/api/repository/CaseDefinitionIdentityLinkCollectionResource.java:78
@ApiOperation(value = "Add a candidate starter to a case definition", tags = { "Case Definitions" },
notes = "It is possible to add either a user or a group.", code = 201)
@ApiResponses(value = {
@ApiResponse(code = 201, message = "Indicates the case definition was found and the identity link was created."),
@ApiResponse(code = 400, message = "Indicates the body does not contain the correct information."),
@ApiResponse(code = 404, message = "Indicates the requested case definition was not found.")
})
@PostMapping(value = "/cmmn-repository/case-definitions/{caseDefinitionId}/identitylinks", produces = "application/json")
@ResponseStatus(HttpStatus.CREATED)
public RestIdentityLink createIdentityLink(@ApiParam(name = "caseDefinitionId") @PathVariable String caseDefinitionId, @RequestBody RestIdentityLink identityLink) {
CaseDefinition caseDefinition = getCaseDefinitionFromRequestWithoutAccessCheck(caseDefinitionId);
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 (restApiInterceptor != null) {
restApiInterceptor.createCaseDefinitionIdentityLink(caseDefinition, identityLink);
}
if (identityLink.getGroup() != null) {
repositoryService.addCandidateStarterGroup(caseDefinition.getId(), identityLink.getGroup());
} else {
repositoryService.addCandidateStarterUser(caseDefinition.getId(), identityLink.getUser());
}
// Always candidate for case definition. User-provided value is ignored
identityLink.setType(IdentityLinkType.CANDIDATE);
return restResponseFactory.createRestIdentityLink(identityLink.getType(), identityLink.getUser(), identityLink.getGroup(), null, caseDefinition.getId(), null);
}
View on GitHub (pinned to d6d39ce1c6)
Solutions
- Send exactly one of 'user' or 'group'; issue two separate POSTs if both are needed.
- Strip the unused field in the client before serializing the payload.
- If the intent is 'user who is member of group', just send the user and let group membership resolve it.
Example fix
// before
{"type":"candidate","user":"john","group":"sales"}
// after
{"type":"candidate","user":"john"}
// second call if needed:
{"type":"candidate","group":"sales"} Defensive patterns
Strategy: validation
Validate before calling
function validateIdentityLink(link) {
if (link.user && link.group) throw new Error('Send user OR group, not both');
if (!link.user && !link.group) throw new Error('Send user or group');
return true;
} Type guard
const isExclusiveIdentityLink = (l) => Boolean(l) && (Boolean(l.user) !== Boolean(l.group));
Try / catch
try {
await post(identityLinksUrl, link);
} catch (e) {
if (e.response?.status === 400 && /Only one of user or group/.test(e.response.data?.message)) {
// split into two posts, one per subject
}
throw e;
} Prevention
- Normalize payloads to a single subject field before sending.
- Issue one request per identity link instead of batching user+group.
- Add a unit test asserting user XOR group in serialized payloads.
When it happens
Trigger: POST /cmmn-repository/case-definitions/{caseDefinitionId}/identity-links with a body containing both fields, e.g. {"type":"candidate","user":"john","group":"sales"}.
Common situations: Clients that echo back a whole identity-link object populated with defaults; forms that always submit both inputs; generic sync tools mapping an ACL entry with both subject types onto one link.
Related errors
- A group or a user is required to create an identity link.
- User or group are required.
- Only one value of user or group is supported.
- The identity link type is required.
- Identity link family should be 'users' or 'groups'.
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/da5d8a6d169d57ad.
Report an issue: GitHub.