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

  1. Use the exact plural segment in the URL: .../identitylinks/users/{identityId}/{type} or .../identitylinks/groups/{identityId}/{type}.
  2. Check casing — the comparison is against the literal lowercase constants; normalize any dynamic URL construction.
  3. 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

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


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)