{"record":{"id":"d3ef0134d55a1f6f","repo":"flowable/flowable-engine","slug":"identity-link-family-should-be-users-or-groups-d3ef01","errorCode":null,"errorMessage":"Identity link family should be 'users' or 'groups'.","messagePattern":"Identity link family should be 'users' or 'groups'\\.","errorType":"exception","errorClass":"FlowableIllegalArgumentException","httpStatus":400,"severity":"error","filePath":"modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/process/ProcessInstanceIdentityLinkResource.java","lineNumber":100,"sourceCode":"        validateIdentityLinkArguments(family, identityId, type);\n\n        IdentityLink link = getIdentityLink(identityId, family, type, processInstance.getId());\n\n        if (restApiInterceptor != null) {\n            restApiInterceptor.deleteProcessInstanceIdentityLink(processInstance, link);\n        }\n\n        if (RestUrls.SEGMENT_IDENTITYLINKS_FAMILY_GROUPS.equals(family)) {\n            runtimeService.deleteGroupIdentityLink(processInstance.getId(), identityId, type);\n            \n        } else {\n            runtimeService.deleteUserIdentityLink(processInstance.getId(), identityId, type);\n        }\n    }\n\n    protected void validateIdentityLinkArguments(String family, String identityId, String type) {\n        if (family == null || (!RestUrls.SEGMENT_IDENTITYLINKS_FAMILY_GROUPS.equals(family) && !RestUrls.SEGMENT_IDENTITYLINKS_FAMILY_USERS.equals(family))) {\n            throw new FlowableIllegalArgumentException(\"Identity link family should be 'users' or 'groups'.\");\n        }\n        if (identityId == null) {\n            throw new FlowableIllegalArgumentException(\"IdentityId is required.\");\n        }\n        if (type == null) {\n            throw new FlowableIllegalArgumentException(\"Type is required.\");\n        }\n    }\n\n    protected IdentityLink getIdentityLink(String identityId, String family, String type, String processInstanceId) {\n        // Perhaps it would be better to offer getting a single identity link\n        // from the API\n        List<IdentityLink> allLinks = runtimeService.getIdentityLinksForProcessInstance(processInstanceId);\n        for (IdentityLink link : allLinks) {\n            if (RestUrls.SEGMENT_IDENTITYLINKS_FAMILY_USERS.equals(family) && identityId.equals(link.getUserId()) && link.getType().equals(type)) {\n                return link;\n            \n            } else if (RestUrls.SEGMENT_IDENTITYLINKS_FAMILY_GROUPS.equals(family) && identityId.equals(link.getGroupId()) && link.getType().equals(type)) {","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/process/ProcessInstanceIdentityLinkResource.java#L82-L118","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nDELETE /runtime/process-instances/pi-1/identitylinks/user/kermit/participant\n// after\nDELETE /runtime/process-instances/pi-1/identitylinks/users/kermit/participant","handlingStrategy":"validation","validationCode":"if (!\"users\".equals(family) && !\"groups\".equals(family)) {\n    throw new IllegalArgumentException(\"family must be 'users' or 'groups', got: \" + family);\n}","typeGuard":null,"tryCatchPattern":"try {\n    restClient.deleteIdentityLink(instanceId, family, identityId, type);\n} catch (HttpClientErrorException.BadRequest e) {\n    if (e.getResponseBodyAsString().contains(\"Identity link family\")) {\n        // fix URL segment to plural lowercase form\n    }\n}","preventionTips":["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."],"tags":["rest-api","identity-link","invalid-url-format","java"],"backgroundTag":"invalid-argument-value","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-18T11:17:12.947Z"}