flowable/flowable-engine · error · FlowableIllegalArgumentException
Identity link family should be 'users' or 'groups'.
Error message
Identity link family should be 'users' or 'groups'.
What it means
Single identity-link endpoints are routed with a URL segment identifying the link 'family' — 'users' or 'groups' (RestUrls.SEGMENT_IDENTITYLINKS_FAMILY_*). If the family path variable is null or any other string, validateIdentityLinkArguments throws FlowableIllegalArgumentException('Identity link family should be \'users\' or \'groups\'.'). It is shared by GET (getIdentityLinkRequest) and DELETE (deleteIdentityLink) handlers.
Solutions
- Use the exact plural segment in the URL: .../identitylinks/users/{identityId}/{type} or .../identitylinks/groups/{identityId}/{type}.
- Check casing — the comparison is against the literal lowercase constants; normalize any dynamic URL construction.
- Verify you are using Flowable 6 REST URL patterns, not legacy Activiti 5 paths.
Example fix
// before DELETE /runtime/process-instances/pi-1/identitylinks/user/kermit/participant // after DELETE /runtime/process-instances/pi-1/identitylinks/users/kermit/participant
Defensive patterns
Strategy: validation
Validate before calling
if (!"users".equals(family) && !"groups".equals(family)) {
throw new IllegalArgumentException("family must be 'users' or 'groups', got: " + family);
} Try / catch
try {
restClient.deleteIdentityLink(instanceId, family, identityId, type);
} catch (HttpClientErrorException.BadRequest e) {
if (e.getResponseBodyAsString().contains("Identity link family")) {
// fix URL segment to plural lowercase form
}
} Prevention
- Always build these URLs from RestUrls constants, not hand-typed strings.
- Use plural lowercase segments: users/ and groups/.
- Add URL-template tests when upgrading between Flowable versions.
When it happens
Trigger: GET/DELETE /runtime/process-instances/{id}/identitylinks/{family}/{identityId}/{type} where {family} is not exactly 'users' or 'groups' — e.g. .../identitylinks/user/kermit/participant or .../identitylinks/User/kermit/participant. Thrown at ProcessInstanceIdentityLinkResource.java:100.
Common situations: Using singular forms ('user'/'group') in the URL; wrong casing; old API paths from other engines (Activiti-style URLs); template bugs where the family variable is interpolated as null or empty.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Only one value of user or group is supported.
- The identity link type is required.
- User or group are required.
- A group or a user is required to create an identity link.
- caseDefinitionId is null
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/d3ef0134d55a1f6f.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/process/ProcessInstanceIdentityLinkResource.java:100
validateIdentityLinkArguments(family, identityId, type);
IdentityLink link = getIdentityLink(identityId, family, type, processInstance.getId());
if (restApiInterceptor != null) {
restApiInterceptor.deleteProcessInstanceIdentityLink(processInstance, link);
}
if (RestUrls.SEGMENT_IDENTITYLINKS_FAMILY_GROUPS.equals(family)) {
runtimeService.deleteGroupIdentityLink(processInstance.getId(), identityId, type);
} else {
runtimeService.deleteUserIdentityLink(processInstance.getId(), identityId, type);
}
}
protected void validateIdentityLinkArguments(String family, String identityId, String type) {
if (family == null || (!RestUrls.SEGMENT_IDENTITYLINKS_FAMILY_GROUPS.equals(family) && !RestUrls.SEGMENT_IDENTITYLINKS_FAMILY_USERS.equals(family))) {
throw new FlowableIllegalArgumentException("Identity link family should be 'users' or 'groups'.");
}
if (identityId == null) {
throw new FlowableIllegalArgumentException("IdentityId is required.");
}
if (type == null) {
throw new FlowableIllegalArgumentException("Type is required.");
}
}
protected IdentityLink getIdentityLink(String identityId, String family, String type, String processInstanceId) {
// Perhaps it would be better to offer getting a single identity link
// from the API
List<IdentityLink> allLinks = runtimeService.getIdentityLinksForProcessInstance(processInstanceId);
for (IdentityLink link : allLinks) {
if (RestUrls.SEGMENT_IDENTITYLINKS_FAMILY_USERS.equals(family) && identityId.equals(link.getUserId()) && link.getType().equals(type)) {
return link;
} else if (RestUrls.SEGMENT_IDENTITYLINKS_FAMILY_GROUPS.equals(family) && identityId.equals(link.getGroupId()) && link.getType().equals(type)) {View on GitHub (pinned to d6d39ce1c6)